Merge remote-tracking branch 'sakthivelvelumani/ue-trs-support' into integration_2026_w28

UE: implement TRS processing in PHY (#199)

This PR implements PHY layer processing for CSI-RS tracking signal that
estimates the CFO and sends a frequency correction command to the radio
when CFO goes above a threshold of 500Hz.

A CI test is added with a GNU radio script between OCUDU gNB and the UE
that simulates a continuous frequency offset. The test passes if the UE
sends a frequency correction command.
Reviewed-by: default avatarRobert Schmidt <robert.schmidt@openairinterface.org>
Reviewed-by: default avatarBartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
Reviewed-by: default avatarFrancesco Mani <email@francescomani.it>
parents aff84037 0f45f471
...@@ -215,9 +215,8 @@ static void UE_synch(void *arg) { ...@@ -215,9 +215,8 @@ static void UE_synch(void *arg) {
((ret.rx_offset << 1) / fp->samples_per_subframe * fp->slots_per_subframe) ((ret.rx_offset << 1) / fp->samples_per_subframe * fp->slots_per_subframe)
+ round((float)((ret.rx_offset << 1) % fp->samples_per_subframe) / fp->samples_per_slot0); + round((float)((ret.rx_offset << 1) % fp->samples_per_subframe) / fp->samples_per_slot0);
if (get_nrUE_params()->cont_fo_comp) { UE->freq_offset = freq_offset - UE->dl_Doppler_shift;
UE->freq_offset = freq_offset - UE->dl_Doppler_shift; if (!get_nrUE_params()->cont_fo_comp) {
} else {
// rerun with new cell parameters and frequency-offset // rerun with new cell parameters and frequency-offset
nrue_ru_set_freq(UE, ul_carrier, dl_carrier, freq_offset); nrue_ru_set_freq(UE, ul_carrier, dl_carrier, freq_offset);
} }
...@@ -711,6 +710,18 @@ static inline int get_readBlockSize(uint16_t slot, const NR_DL_FRAME_PARMS *fp) ...@@ -711,6 +710,18 @@ static inline int get_readBlockSize(uint16_t slot, const NR_DL_FRAME_PARMS *fp)
return rem_samples + next_slot_first_symbol; return rem_samples + next_slot_first_symbol;
} }
void trs_freq_correction(PHY_VARS_NR_UE *ue, int cfo)
{
if (abs(cfo) > TRS_CFO_THRESH) {
LOG_A(PHY, "CFO estimated (%d) from TRS exceeded threshold (%d). Adjusting radio CF\n", cfo, TRS_CFO_THRESH);
ue->freq_offset += cfo;
uint64_t dl_carrier;
uint64_t ul_carrier;
nr_get_carrier_frequencies(ue, &dl_carrier, &ul_carrier);
nrue_ru_set_freq(ue, ul_carrier, dl_carrier, ue->freq_offset);
}
}
void *UE_thread(void *arg) void *UE_thread(void *arg)
{ {
//this thread should be over the processing thread to keep in real time //this thread should be over the processing thread to keep in real time
......
...@@ -463,6 +463,7 @@ typedef struct { ...@@ -463,6 +463,7 @@ typedef struct {
uint8_t power_control_offset; // Ratio of PDSCH EPRE to NZP CSI-RSEPRE [3GPP TS 38.214, sec 5.2.2.3.1], Value: 0->23 representing -8 to 15 dB in 1dB steps; 255: L1 is configured with ProfileSSS uint8_t power_control_offset; // Ratio of PDSCH EPRE to NZP CSI-RSEPRE [3GPP TS 38.214, sec 5.2.2.3.1], Value: 0->23 representing -8 to 15 dB in 1dB steps; 255: L1 is configured with ProfileSSS
uint8_t power_control_offset_ss; // Ratio of NZP CSI-RS EPRE to SSB/PBCH block EPRE [3GPP TS 38.214, sec 5.2.2.3.1], Values: 0: -3dB; 1: 0dB; 2: 3dB; 3: 6dB; 255: L1 is configured with ProfileSSS uint8_t power_control_offset_ss; // Ratio of NZP CSI-RS EPRE to SSB/PBCH block EPRE [3GPP TS 38.214, sec 5.2.2.3.1], Values: 0: -3dB; 1: 0dB; 2: 3dB; 3: 6dB; 255: L1 is configured with ProfileSSS
uint8_t measurement_bitmap; // bit 0 RSRP, bit 1 RI, bit 2 LI, bit 3 PMI, bit 4 CQI, bit 5 i1 uint8_t measurement_bitmap; // bit 0 RSRP, bit 1 RI, bit 2 LI, bit 3 PMI, bit 4 CQI, bit 5 i1
uint8_t last_trs_slot; // indicates to PHY if the slot is end of TRS burst
} fapi_nr_dl_config_csirs_pdu_rel15_t; } fapi_nr_dl_config_csirs_pdu_rel15_t;
typedef enum{vrb_to_prb_mapping_non_interleaved = 0, vrb_to_prb_mapping_interleaved = 1} vrb_to_prb_mapping_t; typedef enum{vrb_to_prb_mapping_non_interleaved = 0, vrb_to_prb_mapping_interleaved = 1} vrb_to_prb_mapping_t;
......
This diff is collapsed.
...@@ -56,6 +56,11 @@ ...@@ -56,6 +56,11 @@
// (0 + 0 * 20) % 512 = 0 // (0 + 0 * 20) % 512 = 0
#define NUM_PROCESS_SLOT_TX_BARRIERS 512 #define NUM_PROCESS_SLOT_TX_BARRIERS 512
// CSI for tracking can have up to 2 resources per slot
#define MAX_CSI_RES_SLOT 2
// Threshold to change radio frequency
#define TRS_CFO_THRESH 500
#include "impl_defs_nr.h" #include "impl_defs_nr.h"
#include "time_meas.h" #include "time_meas.h"
#include "PHY/CODING/coding_defs.h" #include "PHY/CODING/coding_defs.h"
...@@ -553,7 +558,8 @@ typedef struct nr_phy_data_s { ...@@ -553,7 +558,8 @@ typedef struct nr_phy_data_s {
int n_dlsch_codewords; int n_dlsch_codewords;
// Sidelink Rx action decided by MAC // Sidelink Rx action decided by MAC
sl_nr_rx_config_type_enum_t sl_rx_action; sl_nr_rx_config_type_enum_t sl_rx_action;
NR_UE_CSI_RS csirs_vars; int num_csirs;
NR_UE_CSI_RS csirs_vars[MAX_CSI_RES_SLOT];
NR_UE_CSI_IM csiim_vars; NR_UE_CSI_IM csiim_vars;
} nr_phy_data_t; } nr_phy_data_t;
......
...@@ -38,7 +38,8 @@ ...@@ -38,7 +38,8 @@
FN(ULSCH_INTERLEAVING_STATS),\ FN(ULSCH_INTERLEAVING_STATS),\
FN(ULSCH_ENCODING_STATS),\ FN(ULSCH_ENCODING_STATS),\
FN(OFDM_MOD_STATS),\ FN(OFDM_MOD_STATS),\
FN(PRACH_GEN_STATS) FN(PRACH_GEN_STATS),\
FN(TRS_PROCESSING)
typedef enum { typedef enum {
FOREACH_NR_PHY_CPU_MEAS(NOOP), FOREACH_NR_PHY_CPU_MEAS(NOOP),
......
...@@ -134,7 +134,12 @@ void nr_ue_csi_im_procedures(PHY_VARS_NR_UE *ue, ...@@ -134,7 +134,12 @@ void nr_ue_csi_im_procedures(PHY_VARS_NR_UE *ue,
void nr_ue_csi_rs_procedures(PHY_VARS_NR_UE *ue, void nr_ue_csi_rs_procedures(PHY_VARS_NR_UE *ue,
const UE_nr_rxtx_proc_t *proc, const UE_nr_rxtx_proc_t *proc,
const c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP], const c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP],
fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu); fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu,
c16_t trs_estimates[][1][ue->frame_parms.ofdm_symbol_size],
const int res_idx,
const int trs_sym0);
void trs_freq_correction(PHY_VARS_NR_UE *ue, int cfo);
int psbch_pscch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_data_t *phy_data); int psbch_pscch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_data_t *phy_data);
void phy_procedures_nrUE_SL_TX(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_data_tx_t *phy_data, c16_t **txp); void phy_procedures_nrUE_SL_TX(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_data_tx_t *phy_data, c16_t **txp);
......
...@@ -150,8 +150,14 @@ static void nr_ue_scheduled_response_dl(NR_UE_MAC_INST_t *mac, ...@@ -150,8 +150,14 @@ static void nr_ue_scheduled_response_dl(NR_UE_MAC_INST_t *mac,
phy_data->csiim_vars.active = true; phy_data->csiim_vars.active = true;
break; break;
case FAPI_NR_DL_CONFIG_TYPE_CSI_RS: case FAPI_NR_DL_CONFIG_TYPE_CSI_RS:
phy_data->csirs_vars.csirs_config_pdu = pdu->csirs_config_pdu.csirs_config_rel15; AssertFatal(phy_data->num_csirs < MAX_CSI_RES_SLOT, "CSI resources per slot exceeded limit\n");
phy_data->csirs_vars.active = true; const int c = phy_data->num_csirs;
if (phy_data->csirs_vars[c].active) {
AssertFatal(false, "Resource should not be active before its configured\n");
}
phy_data->csirs_vars[c].csirs_config_pdu = pdu->csirs_config_pdu.csirs_config_rel15;
phy_data->csirs_vars[c].active = true;
phy_data->num_csirs++;
break; break;
case FAPI_NR_DL_CONFIG_TYPE_RA_DLSCH: case FAPI_NR_DL_CONFIG_TYPE_RA_DLSCH:
case FAPI_NR_DL_CONFIG_TYPE_SI_DLSCH: case FAPI_NR_DL_CONFIG_TYPE_SI_DLSCH:
......
...@@ -1184,16 +1184,35 @@ void pdsch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_ ...@@ -1184,16 +1184,35 @@ void pdsch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_
} }
// do procedures for CSI-RS // do procedures for CSI-RS
if (phy_data->csirs_vars.active == 1) { {
for(int symb = 0; symb < ue->frame_parms.symbols_per_slot; symb++) { /*
if(is_csi_rs_in_symbol(phy_data->csirs_vars.csirs_config_pdu, symb)) { CSI-RS for tracking use only one port.
if (!slot_fep_map[symb]) { Number of CSI-RS resources for tracking is always 2 per slot.
nr_slot_fep(ue, &ue->frame_parms, proc->nr_slot_rx, symb, rxdataF, link_type_dl, 0, ue->common_vars.rxdata); Computed estimates from first resource is saved and used while estimating second resource.
slot_fep_map[symb] = true; */
c16_t trs_estimates[ue->frame_parms.nb_antennas_rx][1][ue->frame_parms.ofdm_symbol_size];
for (int res = 0; res < MAX_CSI_RES_SLOT; res++) {
if (phy_data->csirs_vars[res].active == 1) {
for (int symb = 0; symb < ue->frame_parms.symbols_per_slot; symb++) {
if (is_csi_rs_in_symbol(phy_data->csirs_vars[res].csirs_config_pdu, symb)) {
if (!slot_fep_map[symb]) {
nr_slot_fep(ue, &ue->frame_parms, proc->nr_slot_rx, symb, rxdataF, link_type_dl, 0, ue->common_vars.rxdata);
slot_fep_map[symb] = true;
}
}
} }
if (res > 0 && phy_data->csirs_vars[res].csirs_config_pdu.csi_type == 0) // tracking CSI
AssertFatal(phy_data->csirs_vars[res - 1].active && (phy_data->csirs_vars[res - 1].csirs_config_pdu.csi_type == 0),
"CSI-RS for tracking must have two consecutive active resources\n");
nr_ue_csi_rs_procedures(ue,
proc,
rxdataF,
&phy_data->csirs_vars[res].csirs_config_pdu,
trs_estimates,
res,
(res == 1) ? phy_data->csirs_vars[0].csirs_config_pdu.symb_l0 : -1);
} }
} }
nr_ue_csi_rs_procedures(ue, proc, rxdataF, &phy_data->csirs_vars.csirs_config_pdu);
} }
int16_t *llr[2]; int16_t *llr[2];
......
...@@ -68,3 +68,7 @@ void configure_nr_nfapi_vnf(eth_params_t params) ...@@ -68,3 +68,7 @@ void configure_nr_nfapi_vnf(eth_params_t params)
{ {
UNUSED(params); UNUSED(params);
} }
void trs_freq_correction(PHY_VARS_NR_UE *ue, int cfo)
{
}
...@@ -1114,7 +1114,8 @@ static void schedule_ta_command(fapi_nr_dl_config_request_t *dl_config, NR_UE_MA ...@@ -1114,7 +1114,8 @@ static void schedule_ta_command(fapi_nr_dl_config_request_t *dl_config, NR_UE_MA
static NR_CSI_ResourceConfigId_t find_CSI_resourceconfig(NR_CSI_MeasConfig_t *csi_measconfig, static NR_CSI_ResourceConfigId_t find_CSI_resourceconfig(NR_CSI_MeasConfig_t *csi_measconfig,
NR_BWP_Id_t dl_bwp_id, NR_BWP_Id_t dl_bwp_id,
NR_NZP_CSI_RS_ResourceId_t csi_id) NR_NZP_CSI_RS_ResourceId_t csi_id,
bool *is_last_res)
{ {
bool found = false; bool found = false;
for (int csi_list = 0; csi_list < csi_measconfig->csi_ResourceConfigToAddModList->list.count; csi_list++) { for (int csi_list = 0; csi_list < csi_measconfig->csi_ResourceConfigToAddModList->list.count; csi_list++) {
...@@ -1139,14 +1140,14 @@ static NR_CSI_ResourceConfigId_t find_CSI_resourceconfig(NR_CSI_MeasConfig_t *cs ...@@ -1139,14 +1140,14 @@ static NR_CSI_ResourceConfigId_t find_CSI_resourceconfig(NR_CSI_MeasConfig_t *cs
AssertFatal(csi_res->nzp_CSI_RS_Resources.list.array[k], "NZP_CSI_RS_ResourceId shoulan't be NULL\n"); AssertFatal(csi_res->nzp_CSI_RS_Resources.list.array[k], "NZP_CSI_RS_ResourceId shoulan't be NULL\n");
if (csi_id == *csi_res->nzp_CSI_RS_Resources.list.array[k]) { if (csi_id == *csi_res->nzp_CSI_RS_Resources.list.array[k]) {
found = true; found = true;
*is_last_res = (k == (csi_res->nzp_CSI_RS_Resources.list.count - 1));
break; break;
} }
} }
if (found && csi_res->trs_Info) if (found && csi_res->trs_Info)
// CRI-RS for Tracking (not implemented yet) /* CRI-RS for Tracking. In this case there is no associated CSI report
// in this case we there is no associated CSI report * therefore to signal this we return a value higher than
// therefore to signal this we return a value higher than * maxNrofCSI-ResourceConfigurations. */
// maxNrofCSI-ResourceConfigurations
return NR_maxNrofCSI_ResourceConfigurations + 1; return NR_maxNrofCSI_ResourceConfigurations + 1;
else if (found) else if (found)
return csires->csi_ResourceConfigId; return csires->csi_ResourceConfigId;
...@@ -1216,7 +1217,8 @@ static void nr_schedule_csirs_reception(NR_UE_MAC_INST_t *mac, int frame, int sl ...@@ -1216,7 +1217,8 @@ static void nr_schedule_csirs_reception(NR_UE_MAC_INST_t *mac, int frame, int sl
csi_period_offset(NULL, nzpcsi->periodicityAndOffset, &period, &offset); csi_period_offset(NULL, nzpcsi->periodicityAndOffset, &period, &offset);
if((frame * mac->frame_structure.numb_slots_frame + slot-offset) % period != 0) if((frame * mac->frame_structure.numb_slots_frame + slot-offset) % period != 0)
continue; continue;
NR_CSI_ResourceConfigId_t csi_res_id = find_CSI_resourceconfig(csi_measconfig, dl_bwp_id, nzpcsi->nzp_CSI_RS_ResourceId); bool is_last_res = false;
NR_CSI_ResourceConfigId_t csi_res_id = find_CSI_resourceconfig(csi_measconfig, dl_bwp_id, nzpcsi->nzp_CSI_RS_ResourceId, &is_last_res);
// do not schedule reseption of this CSI-RS if not associated with current BWP // do not schedule reseption of this CSI-RS if not associated with current BWP
if(csi_res_id < 0) if(csi_res_id < 0)
continue; continue;
...@@ -1226,9 +1228,12 @@ static void nr_schedule_csirs_reception(NR_UE_MAC_INST_t *mac, int frame, int sl ...@@ -1226,9 +1228,12 @@ static void nr_schedule_csirs_reception(NR_UE_MAC_INST_t *mac, int frame, int sl
csirs_config_pdu->subcarrier_spacing = mu; csirs_config_pdu->subcarrier_spacing = mu;
csirs_config_pdu->cyclic_prefix = current_DL_BWP->cyclicprefix ? *current_DL_BWP->cyclicprefix : 0; csirs_config_pdu->cyclic_prefix = current_DL_BWP->cyclicprefix ? *current_DL_BWP->cyclicprefix : 0;
if (csi_res_id > NR_maxNrofCSI_ResourceConfigurations) if (csi_res_id > NR_maxNrofCSI_ResourceConfigurations) {
/* According to 38.214 5.1.6.1.1, the number of resources indicate if one
* or two consequtive slots for TRS is used. We indicate to phy the last slot. */
csirs_config_pdu->last_trs_slot = is_last_res;
csirs_config_pdu->csi_type = 0; // TRS csirs_config_pdu->csi_type = 0; // TRS
else } else
csirs_config_pdu->csi_type = 1; // NZP-CSI-RS csirs_config_pdu->csi_type = 1; // NZP-CSI-RS
csirs_config_pdu->scramb_id = nzpcsi->scramblingID; csirs_config_pdu->scramb_id = nzpcsi->scramblingID;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment