#include "PHY/defs_nr_UE.h" #include "PHY/TOOLS/tools_defs.h" #include "PHY/NR_REFSIG/sss_nr.h" #include "PHY/NR_UE_ESTIMATION/nr_estimation.h" #include "PHY/MODULATION/modulation_UE.h" #include "PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h" #include "SCHED_NR_UE/defs.h" // Number of symbols carrying SLSS signal - PSS+SSS+PSBCH #define SL_NR_NUMSYM_SLSS_NORMAL_CP 14 #define SL_NR_MAX_RX_ANTENNA 1 #define SL_NR_FIRST_PSS_SYMBOL 1 #define SL_NR_FIRST_SSS_SYMBOL 3 #define SL_NR_NUM_PSS_SSS_SYMBOLS 4 // #define SL_DEBUG static int sl_nr_pss_correlation(PHY_VARS_NR_UE *UE, int frame_index) { sl_nr_ue_phy_params_t *sl_ue = &UE->SL_UE_PHY_PARAMS; SL_NR_SYNC_PARAMS_t *sync_params = &sl_ue->sync_params; NR_DL_FRAME_PARMS *sl_fp = &UE->SL_UE_PHY_PARAMS.sl_frame_params; int16_t **pss_for_correlation = (int16_t **)sl_ue->init_params.sl_pss_for_correlation; uint32_t length = (frame_index == 0) ? sl_fp->samples_per_frame + (2 * sl_fp->ofdm_symbol_size) : sl_fp->samples_per_frame; int32_t **rxdata = (int32_t **)UE->common_vars.rxdata; #ifdef SL_DEBUG char fname[50], sname[25]; sprintf(fname, "rxdata_frame_%d.m", frame_index); sprintf(sname, "rxd_frame%d", frame_index); LOG_M(fname, sname, &rxdata[0][frame_index * sl_fp->samples_per_frame], sl_fp->samples_per_frame, 1, 1); LOG_M("pss_for_correlation0.m", "pss_id0", pss_for_correlation[0], 2048, 1, 1); LOG_M("pss_for_correlation1.m", "pss_id1", pss_for_correlation[1], 2048, 1, 1); int64_t *pss_corr_debug_values[SL_NR_NUM_IDs_IN_PSS]; #endif int maxval = 0; for (int i = 0; i < 2 * (sl_fp->ofdm_symbol_size); i++) { maxval = max(maxval, pss_for_correlation[0][i]); maxval = max(maxval, -pss_for_correlation[0][i]); maxval = max(maxval, pss_for_correlation[1][i]); maxval = max(maxval, -pss_for_correlation[1][i]); } int shift = log2_approx(maxval); //*(sl_fp->ofdm_symbol_size + sl_fp->nb_prefix_samples)*2); #ifdef SL_DEBUG LOG_I(PHY, "SIDELINK SLSS SEARCH: Function:%s\n", __func__); LOG_I(PHY, "maxval:%d, shift:%d\n", maxval, shift); #endif int64_t avg[SL_NR_NUM_IDs_IN_PSS] = {0}; int64_t peak_value = 0, psss_corr_value = 0; unsigned int peak_position = 0, pss_source = 0; for (int pss_index = 0; pss_index < SL_NR_NUM_IDs_IN_PSS; pss_index++) avg[pss_index] = 0; #ifdef SL_DEBUG int64_t *pss_corr_debug_values[SL_NR_NUM_IDs_IN_PSS]; for (int pss_index = 0; pss_index < SL_NR_NUM_IDs_IN_PSS; pss_index++) pss_corr_debug_values[pss_index] = malloc16_clear(length * sizeof(int64_t)); #endif for (int n = 0; n < length - sl_fp->ofdm_symbol_size; n += 4) { // for (int pss_index = 0; pss_index < SL_NR_NUM_IDs_IN_PSS; pss_index++) { psss_corr_value = 0; // calculate dot product of primary_synchro_time_nr and rxdata[ar][n] (ar=0..nb_ant_rx) and store the sum in temp[n]; for (int ar = 0; ar < sl_fp->nb_antennas_rx; ar++) { /* perform correlation of rx data and pss sequence ie it is a dot product */ const c32_t result = dot_product((c16_t *)pss_for_correlation[pss_index], (c16_t *)&(rxdata[ar][n + frame_index * sl_fp->samples_per_frame]), sl_fp->ofdm_symbol_size, shift); const c64_t r64 = {.r = result.r, .i = result.i}; psss_corr_value += squaredMod(r64); #ifdef SL_DEBUG pss_corr_debug_values[pss_index][n] = psss_corr_value; printf("frame:%d n:%d, pss_index:%d, pss_for_correlation[pss_index][0]:%x, rxdata[n]:%x\n", frame_index, n, pss_index, pss_for_correlation[pss_index][0], rxdata[ar][n + frame_index * sl_fp->samples_per_frame]); printf("result %lld, pss_corr_values[%d][%d]:%ld\n", result, pss_index, n, pss_corr_debug_values[pss_index][n]); printf("pss_index %d: n %6u peak_value %15llu\n", pss_index, n, (unsigned long long)pss_corr_debug_values[pss_index][n]); printf("peak_value:%ld, peak_position:%d, pss_source:%d\n", peak_value, peak_position, pss_source); #endif } // calculate the absolute value of sync_corr[n] avg[pss_index] += psss_corr_value; if (psss_corr_value > peak_value) { peak_value = psss_corr_value; peak_position = n; pss_source = pss_index; #ifdef SL_DEBUG printf("pss_index %d: n %6u peak_value %15llu\n", pss_index, n, (unsigned long long)psss_corr_value); #endif } } } #ifdef SL_DEBUG LOG_M("pss_corr_debug_values_0.m", "pss_corr0", &pss_corr_debug_values[0][0], length, 1, 6); LOG_M("pss_corr_debug_values_1.m", "pss_corr1", &pss_corr_debug_values[1][0], length, 1, 6); for (int pss_index = 0; pss_index < SL_NR_NUM_IDs_IN_PSS; pss_index++) { free(pss_corr_debug_values[pss_index]); } #endif double ffo_est = 0; if (UE->UE_fo_compensation) { // Not tested // fractional frequency offset computation according to Cross-correlation Synchronization Algorithm Using PSS // Shoujun Huang, Yongtao Su, Ying He and Shan Tang, "Joint time and frequency offset estimation in LTE downlink," 7th // International Conference on Communications and Networking in China, 2012. c16_t *pss = (c16_t *)pss_for_correlation[pss_source]; c16_t *rxd = (c16_t *)&(rxdata[0][peak_position + frame_index * sl_fp->samples_per_frame]); int half_symbol = sl_fp->ofdm_symbol_size >> 1; // Computing cross-correlation at peak on half the symbol size for first half of data c32_t r1 = dot_product(pss, rxd, half_symbol, shift); // Computing cross-correlation at peak on half the symbol size for data shifted by half symbol size // as it is real and complex it is necessary to shift by a value equal to symbol size to obtain such shift c32_t r2 = dot_product(pss + half_symbol, rxd + half_symbol, half_symbol, shift); cd_t r1d = {r1.r, r1.i}, r2d = {r2.r, r2.i}; // estimation of fractional frequency offset: angle[(result1)'*(result2)]/pi ffo_est = atan2(r1d.r * r2d.i - r2d.r * r1d.i, r1d.r * r2d.r + r1d.i * r2d.i) / M_PI; #ifdef SL_DEBUG printf("ffo %lf\n", ffo_est); #endif } // computing absolute value of frequency offset sync_params->freq_offset = ffo_est * sl_fp->subcarrier_spacing; for (int pss_index = 0; pss_index < SL_NR_NUM_IDs_IN_PSS; pss_index++) avg[pss_index] /= (length / 4); sync_params->N_sl_id2 = pss_source; LOG_I(PHY, "PSS Source = %d, Peak found at pos %d, val = %llu (%d dB) avg %d dB, ffo %lf, freq offset:%d Hz\n", pss_source, peak_position, (unsigned long long)peak_value, dB_fixed64(peak_value), dB_fixed64(avg[pss_source]), ffo_est, sync_params->freq_offset); if (peak_value < 5 * avg[pss_source]) return (-1); return peak_position; } static void sl_nr_extract_sss(PHY_VARS_NR_UE *ue, UE_nr_rxtx_proc_t *proc, int32_t *tot_metric, uint8_t *phase_max, c16_t rxdataF[][ue->SL_UE_PHY_PARAMS.sl_frame_params.samples_per_slot_wCP]) { c16_t pss_ext[SL_NR_MAX_RX_ANTENNA][SL_NR_NUM_PSS_SYMBOLS][SL_NR_PSS_SEQUENCE_LENGTH]; c16_t sss_ext[SL_NR_MAX_RX_ANTENNA][SL_NR_NUM_SSS_SYMBOLS][SL_NR_PSS_SEQUENCE_LENGTH]; uint8_t Nid2 = ue->SL_UE_PHY_PARAMS.sync_params.N_sl_id2; NR_DL_FRAME_PARMS *sl_fp = &ue->SL_UE_PHY_PARAMS.sl_frame_params; int16_t *d; uint16_t Nid1 = 0; uint8_t phase; c16_t *rxF_ext; for (int aarx = 0; aarx < sl_fp->nb_antennas_rx; aarx++) { unsigned int ofdm_symbol_size = sl_fp->ofdm_symbol_size; // pss, sss extraction for (int sym = SL_NR_FIRST_PSS_SYMBOL; sym < SL_NR_FIRST_PSS_SYMBOL + SL_NR_NUM_PSS_SSS_SYMBOLS; sym++) { if (sym < SL_NR_FIRST_PSS_SYMBOL + SL_NR_NUM_PSS_SYMBOLS) { rxF_ext = &pss_ext[aarx][sym - SL_NR_FIRST_PSS_SYMBOL][0]; } else { rxF_ext = &sss_ext[aarx][sym - SL_NR_FIRST_SSS_SYMBOL][0]; } unsigned int k = sl_fp->first_carrier_offset + sl_fp->ssb_start_subcarrier + 2; if (k >= ofdm_symbol_size) k -= ofdm_symbol_size; LOG_D(PHY, "firstcarrieroffset:%d, ssb_sc:%d, k:%d, symbol:%d\n", sl_fp->first_carrier_offset, sl_fp->ssb_start_subcarrier, k, sym); for (int i = 0; i < SL_NR_PSS_SEQUENCE_LENGTH; i++) { rxF_ext[i] = rxdataF[aarx][sym * ofdm_symbol_size + k]; k++; if (k == ofdm_symbol_size) k = 0; } } LOG_D(PHY, "SIDELINK SLSS SEARCH: EXTRACTION OF PSS, SSS done\n"); #ifdef SL_DEBUG LOG_M("pss_ext_sym1.m", "pss_ext1", &pss_ext[aarx][0][0], SL_NR_PSS_SEQUENCE_LENGTH, 1, 1); LOG_M("pss_ext_sym2.m", "pss_ext2", &pss_ext[aarx][1][0], SL_NR_PSS_SEQUENCE_LENGTH, 1, 1); LOG_M("sss_ext_sym3.m", "sss_ext3", &sss_ext[aarx][0][0], SL_NR_PSS_SEQUENCE_LENGTH, 1, 1); LOG_M("sss_ext_sym4.m", "sss_ext4", &sss_ext[aarx][1][0], SL_NR_PSS_SEQUENCE_LENGTH, 1, 1); #endif // get conjugated channel estimate from PSS, H* = R* \cdot PSS // and do channel estimation and compensation based on PSS int16_t *pss = ue->SL_UE_PHY_PARAMS.init_params.sl_pss_for_sync[Nid2]; c16_t *pss_ext2, *sss_ext2; // 2 Symbols each for PSS and SSS for (int j = 0; j < SL_NR_NUM_PSS_OR_SSS_SYMBOLS; j++) { sss_ext2 = &sss_ext[aarx][j][0]; pss_ext2 = &pss_ext[aarx][j][0]; for (int i = 0; i < SL_NR_PSS_SEQUENCE_LENGTH; i++) { // This is H*(PSS) = R* \cdot PSS c16_t tmp = {.r = (pss_ext2[i].r * pss[i]), .i = (-pss_ext2[i].i * pss[i])}; int amp = c16amp2(tmp); int shift = log2_approx(amp) / 2; // This is R(SSS) \cdot H*(PSS) c16_t tmp2 = c16mulShift(tmp, sss_ext2[i], shift); // MRC on RX antennas // sss_ext now contains the compensated SSS if (aarx == 0) { sss_ext2[i].r = tmp2.r; sss_ext2[i].i = tmp2.i; } else { AssertFatal(1 == 0, "SIDELINK MORE THAN 1 RX ANTENNA NOT YET SUPPORTED\n"); } } } LOG_D(PHY, "SIDELINK SLSS SEARCH: Ch. estimation SSS done\n"); } /* #ifdef SL_DEBUG write_output("rxsig0.m","rxs0",&ue->common_vars.rxdata[0][0],ue->frame_parms.samples_per_subframe,1,1); write_output("rxdataF0_pss.m","rxF0_pss",&ue->common_vars.rxdataF[0][0],frame_parms->ofdm_symbol_size,1,1); write_output("rxdataF0_sss.m","rxF0_sss",&ue->common_vars.rxdataF[0][(SSS_SYMBOL_NB-PSS_SYMBOL_NB)*frame_parms->ofdm_symbol_size],frame_parms->ofdm_symbol_size,1,1); write_output("pss_ext.m","pss_ext",pss_ext,LENGTH_PSS_NR,1,1); #endif */ /* for phase evaluation, one uses an array of possible phase shifts */ /* then a correlation is done between received signal with a shift pĥase and the reference signal */ /* Computation of signal with shift phase is based on below formula */ /* cosinus cos(x + y) = cos(x)cos(y) - sin(x)sin(y) */ /* sinus sin(x + y) = sin(x)cos(y) + cos(x)sin(y) */ // now do the SSS detection based on the pre computed SSS sequences *tot_metric = INT_MIN; c16_t *sss = &sss_ext[0][0][0]; for (uint16_t id1 = 0; id1 < SL_NR_NUM_IDs_IN_SSS; id1++) { // all possible SSS Nid1 values for (phase = 0; phase < PHASE_HYPOTHESIS_NUMBER; phase++) { // phase offset between PSS and SSS int32_t metric = 0, metric_re = 0; d = (int16_t *)&ue->SL_UE_PHY_PARAMS.init_params.sl_sss_for_sync[Nid2 * SL_NR_NUM_IDs_IN_SSS + id1]; // This is the inner product using one particular value of each unknown parameter for (int i = 0; i < SL_NR_SSS_SEQUENCE_LENGTH; i++) { metric_re += d[i] * (((int64_t)phase_nr[phase].r * sss[i].r - phase_nr[phase].i * sss[i].i) >> 15); } metric = metric_re; // if the current metric is better than the last save it if (metric > *tot_metric) { *tot_metric = metric; Nid1 = id1; *phase_max = phase; LOG_D(PHY, "(phase,Nid1) (%d,%d), metric_phase %d tot_metric %d, phase_max %d \n", phase, Nid1, metric, *tot_metric, *phase_max); } } } ue->SL_UE_PHY_PARAMS.sync_params.N_sl_id1 = Nid1; ue->SL_UE_PHY_PARAMS.sync_params.N_sl_id = ue->SL_UE_PHY_PARAMS.sync_params.N_sl_id1 + SL_NR_NUM_IDs_IN_SSS * ue->SL_UE_PHY_PARAMS.sync_params.N_sl_id2; LOG_I(PHY, "UE[%d]NR-SL SLSS SEARCH: SSS Processing over. id2 from SSS:%d, id1 from PSS:%d, SLSS id:%d\n", ue->Mod_id, ue->SL_UE_PHY_PARAMS.sync_params.N_sl_id1, ue->SL_UE_PHY_PARAMS.sync_params.N_sl_id2, ue->SL_UE_PHY_PARAMS.sync_params.N_sl_id); #ifdef SL_DEBUG #define SSS_METRIC_FLOOR_NR (30000) if (*tot_metric > SSS_METRIC_FLOOR_NR) { Nid2 = ue->SL_UE_PHY_PARAMS.sync_params.N_sl_id2; Nid1 = ue->SL_UE_PHY_PARAMS.sync_params.N_sl_id1; printf("Nid2 %d Nid1 %d tot_metric %d, phase_max %d \n", Nid2, Nid1, *tot_metric, *phase_max); } #endif return; } // Right now 2 frames worth of samples get processed for PSS in OAI. // For PSS in Sidelink, worst case 1 SSB in 16 frames can be present // Hence 16 frames worth of samples needs to be correlated to find the PSS. nr_initial_sync_t sl_nr_slss_search(PHY_VARS_NR_UE *UE, UE_nr_rxtx_proc_t *proc, int num_frames) { sl_nr_ue_phy_params_t *sl_ue = &UE->SL_UE_PHY_PARAMS; SL_NR_SYNC_PARAMS_t *sync_params = &sl_ue->sync_params; NR_DL_FRAME_PARMS *sl_fp = &UE->SL_UE_PHY_PARAMS.sl_frame_params; int32_t sync_pos = -1; // sync_pos_frame = -1; int32_t metric_tdd_ncp = 0; uint8_t phase_tdd_ncp; double im, re; int ret = -1; uint16_t rx_slss_id = 65535; nr_initial_sync_t result = {false, 0}; #ifdef SL_DEBUG_SEARCH_SLSS LOG_D(PHY, "SIDELINK SEARCH SLSS: Function:%s\n", __func__); #endif /* Initial synchronisation * * 1 radio frame = 10 ms * <---------------------------------------------------------------------------> * | Received UE data buffer | * ---------------------------------------------------------------------------- * <-------------->|psbch|pss|pss|sss|sss|psbch sym5-sym 12|sym13 - guard| * sync_pos SS/PSBCH block */ // initial sync performed on 16 successive frames. Worst case - one PSBCH can be sent in 16 frames. // If psbch passes on first frame, no need to process second frame // Problem with the frame approach is that // --------- SSB can be on the boundary between frames. In this case if only 1 SSB is sent we will miss it. // rxdata will hold 16 frames + slot worth of samples. This needs to be processed to find the best SSB for (int frame_index = 0; frame_index < num_frames; frame_index++) { /* process pss search on received buffer */ sync_pos = sl_nr_pss_correlation(UE, frame_index); if (sync_pos == -1) { LOG_I(PHY, "SIDELINK SEARCH SLSS: No PSSS found in this frame\n"); continue; } sync_pos += frame_index * sl_fp->samples_per_frame; // position in the num_frames frame samples for (int pss_sym = 1; pss_sym < 3; pss_sym++) { // Now Sync pos can point to PSS 1st symbol or 2nd symbol. // Right now implemented the strategy to try both locations for FFT // Think about a better correlation strategy if (pss_sym == 1) { // Check if sync pos points to SYMBOL1 - first symbol of PSS location if (sync_pos > sl_fp->nb_prefix_samples0 + sl_fp->ofdm_symbol_size + sl_fp->nb_prefix_samples) sync_params->ssb_offset = sync_pos - (sl_fp->nb_prefix_samples0 + sl_fp->ofdm_symbol_size + sl_fp->nb_prefix_samples); else sync_params->ssb_offset = sync_pos + sl_fp->samples_per_frame - (sl_fp->nb_prefix_samples0 + sl_fp->ofdm_symbol_size + sl_fp->nb_prefix_samples); } else { // Check if sync pos points to SYMBOL2 - second symbol of PSS location if (sync_pos >= sl_fp->nb_prefix_samples0 + 2 * (sl_fp->ofdm_symbol_size + sl_fp->nb_prefix_samples)) sync_params->ssb_offset = sync_pos - (sl_fp->nb_prefix_samples0 + 2 * (sl_fp->ofdm_symbol_size + sl_fp->nb_prefix_samples)); else sync_params->ssb_offset = sync_pos + sl_fp->samples_per_frame - (sl_fp->nb_prefix_samples0 + 2 * (sl_fp->ofdm_symbol_size + sl_fp->nb_prefix_samples)); } LOG_I(PHY, "UE[%d]SIDELINK SEARCH SLSS: PSS Peak at %d, PSS sym:%d, Estimated PSS position %d\n", UE->Mod_id, sync_pos, pss_sym, sync_params->ssb_offset); int slss_block_samples = (SL_NR_NUMSYM_SLSS_NORMAL_CP * sl_fp->ofdm_symbol_size) + (SL_NR_NUMSYM_SLSS_NORMAL_CP - 1) * sl_fp->nb_prefix_samples + sl_fp->nb_prefix_samples0; int ssb_end_position = sync_params->ssb_offset + slss_block_samples; LOG_D(PHY, "ssb_end:%d ssb block samples:%d total samples: %d\n", ssb_end_position, slss_block_samples, num_frames * sl_fp->samples_per_frame); /* check that SSS/PBCH block is continuous inside the received buffer */ if (ssb_end_position < num_frames * sl_fp->samples_per_frame) { // digital compensation of FFO for SSB symbols if (UE->UE_fo_compensation) { // This code to be checked. Why do we do this before PSS detection is successful? double s_time = 1 / (1.0e3 * sl_fp->samples_per_subframe); // sampling time double off_angle = -2 * M_PI * s_time * (sync_params->freq_offset); // offset rotation angle compensation per sample int start = sync_params->ssb_offset; // start for offset correction is at ssb_offset (pss time position) // Adapt this for other numerologies number of symbols with larger cp increases TBD int end = ssb_end_position; // loop over samples in all symbols (ssb size), including prefix LOG_I(PHY, "SLSS SEARCH: FREQ comp of SLSS samples. Freq_OFSET:%d, startpos:%d, end_pos:%d\n", sync_params->freq_offset, start, end); for (int n = start; n < end; n++) { for (int ar = 0; ar < sl_fp->nb_antennas_rx; ar++) { re = ((double)(((short *)UE->common_vars.rxdata[ar]))[2 * n]); im = ((double)(((short *)UE->common_vars.rxdata[ar]))[2 * n + 1]); ((short *)UE->common_vars.rxdata[ar])[2 * n] = (short)(round(re * cos(n * off_angle) - im * sin(n * off_angle))); ((short *)UE->common_vars.rxdata[ar])[2 * n + 1] = (short)(round(re * sin(n * off_angle) + im * cos(n * off_angle))); } } } NR_DL_FRAME_PARMS *frame_parms = &UE->SL_UE_PHY_PARAMS.sl_frame_params; const uint32_t rxdataF_sz = frame_parms->samples_per_slot_wCP; __attribute__((aligned(32))) c16_t rxdataF[frame_parms->nb_antennas_rx][rxdataF_sz]; /* In order to achieve correct processing for NR prefix samples is forced to 0 and then restored after function call */ for (int symbol = 0; symbol < SL_NR_NUMSYM_SLSS_NORMAL_CP; symbol++) { nr_slot_fep(UE, frame_parms, proc->nr_slot_rx, symbol, rxdataF, link_type_sl, sync_params->ssb_offset, UE->common_vars.rxdata); } sl_nr_extract_sss(UE, NULL, &metric_tdd_ncp, &phase_tdd_ncp, rxdataF); // save detected cell id to psbch rx_slss_id = UE->SL_UE_PHY_PARAMS.sync_params.N_sl_id; __attribute__((aligned(32))) struct complex16 dl_ch_estimates[frame_parms->nb_antennas_rx][rxdataF_sz]; __attribute__(( aligned(32))) struct complex16 dl_ch_estimates_time[frame_parms->nb_antennas_rx][frame_parms->ofdm_symbol_size]; uint8_t decoded_output[4]; for (int symbol = 0; symbol < SL_NR_NUMSYM_SLSS_NORMAL_CP - 1;) { nr_pbch_channel_estimation(frame_parms, &UE->SL_UE_PHY_PARAMS, rxdataF_sz, dl_ch_estimates, dl_ch_estimates_time, proc, symbol, symbol, 0, 0, frame_parms->ssb_start_subcarrier, rxdataF, 1, rx_slss_id); symbol = (symbol == 0) ? 5 : symbol + 1; } ret = nr_rx_psbch(UE, proc, rxdataF_sz, dl_ch_estimates, frame_parms, decoded_output, rxdataF, rx_slss_id); result.cell_detected = (ret == 0) ? true : false; if (result.cell_detected) { // Check this later TBD // sync at symbol ue->symbol_offset // computing the offset wrt the beginning of the frame // SSB located at symbol 0 sync_params->remaining_frames = (num_frames * sl_fp->samples_per_frame - sync_params->ssb_offset) / sl_fp->samples_per_frame; // ssb_offset points to start of sl-ssb // rx_offset points to remaining samples needed to fill a frame sync_params->rx_offset = sync_params->ssb_offset % sl_fp->samples_per_frame; LOG_I(PHY, "UE[%d]SIDELINK SLSS SEARCH: PSBCH RX OK. Remainingframes:%d, rx_offset:%d\n", UE->Mod_id, sync_params->remaining_frames, sync_params->rx_offset); uint32_t psbch_payload = (*(uint32_t *)decoded_output); // retrieve DFN and slot number from SL-MIB sync_params->DFN = (((psbch_payload & 0x0700) >> 1) | ((psbch_payload & 0xFE0000) >> 17)); sync_params->slot_offset = (((psbch_payload & 0x010000) >> 10) | ((psbch_payload & 0xFC000000) >> 26)); LOG_I(PHY, "UE[%d]SIDELINK SLSS SEARCH: SL-MIB: DFN:%d, slot:%d.\n", UE->Mod_id, sync_params->DFN, sync_params->slot_offset); UE->adjust_rxgain = nr_sl_psbch_rsrp_measurements(sl_ue, frame_parms, rxdataF, false); UE->init_sync_frame = sync_params->remaining_frames; result.rx_offset = sync_params->rx_offset; nr_sidelink_indication_t sl_indication; sl_nr_rx_indication_t rx_ind = {0}; uint16_t number_pdus = 1; nr_fill_sl_indication(&sl_indication, &rx_ind, NULL, proc, UE, NULL); nr_fill_sl_rx_indication(&rx_ind, SL_NR_RX_PDU_TYPE_SSB, UE, number_pdus, proc, (void *)decoded_output, rx_slss_id); LOG_D(PHY, "Sidelink SLSS SEARCH PSBCH RX OK. Send SL-SSB TO MAC\n"); if (UE->if_inst && UE->if_inst->sl_indication) UE->if_inst->sl_indication(&sl_indication); break; } LOG_I(PHY, "SIDELINK SLSS SEARCH: SLSS ID: %d metric %d, phase %d, psbch CRC %s\n", sl_ue->sync_params.N_sl_id, metric_tdd_ncp, phase_tdd_ncp, (ret == 0) ? "OK" : "NOT OK"); } else { LOG_W(PHY, "SIDELINK SLSS SEARCH: Error: Not enough samples to process PSBCH. sync_pos %d\n", sync_pos); } } if (result.cell_detected) break; } if (!result.cell_detected) { // PSBCH not found so indicate sync to higher layers and configure frame parameters LOG_E(PHY, "SIDELINK SLSS SEARCH: PSBCH not received. Estimated PSS position:%d\n", sync_pos); } return result; }