Merge remote-tracking branch 'origin/develop-measGapConfig' into integration_2025_w29 (!3225)
Measurement gaps Scheduling measurement gaps to provide sufficient time for the UE to re-tune its transceiver to the target carrier, complete the set of measurements and then re-tune its transceiver back to the original carrier.
This commit is contained in:
@@ -1415,6 +1415,11 @@ uint32_t nr_timer_elapsed_time(const NR_timer_t *timer)
|
||||
return timer->counter;
|
||||
}
|
||||
|
||||
uint32_t nr_timer_remaining_time(const NR_timer_t *timer)
|
||||
{
|
||||
return timer->target - timer->counter;
|
||||
}
|
||||
|
||||
void nr_timer_setup(NR_timer_t *timer, const uint32_t target, const uint32_t step)
|
||||
{
|
||||
timer->target = target;
|
||||
|
||||
@@ -222,6 +222,12 @@ bool nr_timer_is_active(const NR_timer_t *timer);
|
||||
* @return Time passed since start of timer
|
||||
*/
|
||||
uint32_t nr_timer_elapsed_time(const NR_timer_t *timer);
|
||||
/**
|
||||
* @brief To return how much time is left until the timer expires
|
||||
* @param timer Timer to be checked
|
||||
* @return Time left until the timer expires
|
||||
*/
|
||||
uint32_t nr_timer_remaining_time(const NR_timer_t *timer);
|
||||
|
||||
int set_default_nta_offset(frequency_range_t freq_range, uint32_t samples_per_subframe);
|
||||
|
||||
|
||||
@@ -139,6 +139,10 @@ These modes of operation are supported:
|
||||
- evaluation of CQI report
|
||||
- MAC scheduling of SR reception
|
||||
- Intra-frequency handover
|
||||
- Inter-frequency handover
|
||||
- Measurement gaps are automatically computed at the DU if the CU has neighbor information and the configured
|
||||
neighbors include cells operating on different frequencies
|
||||
- DUs must be synchronized with each other for the measurements to be properly performed
|
||||
- Initial support for RedCap
|
||||
- Scheduling of SIBs (2, 19)
|
||||
|
||||
|
||||
+2
-2
@@ -291,7 +291,7 @@ Note that while 38.331 requires only one RRC transaction to happen at a time,
|
||||
the 5G RRC does not actually ensure this; it only tries to prevent it by
|
||||
chaining of transactions and some checks. For instance, the Security Mode
|
||||
procedure is followed by a UE capability procedure, and some procedures are not
|
||||
executed if others are ongoing (ex.: handover if there is reconfiguratio).
|
||||
executed if others are ongoing (ex.: handover if there is reconfiguration).
|
||||
However, it might be possible to trigger a procedure while another is ongoing.
|
||||
|
||||
As of now, no queueing mechanims exists to ensure only one operation is
|
||||
@@ -303,7 +303,7 @@ Handover-related data is stored in a per-UE structure of type
|
||||
`nr_handover_context_t`. It is a pointer and only set during handover
|
||||
operation. This data structure has in turn two pointers, one to the source CU
|
||||
(`nr_ho_source_cu_t`), and one to the target CU (`nr_ho_target_cu_t`). In F1,
|
||||
both are present. In N2 and Xn, only one pointer is supposed to be set a the
|
||||
both are present. In N2 and Xn, only one pointer is supposed to be set at the
|
||||
corresponding CU.
|
||||
|
||||
`nr_ho_source_cu_t` contains notably a function pointer `ho_cancel` for
|
||||
|
||||
@@ -123,7 +123,7 @@ You can do handover across DUs with a COTS UE. Note that these DUs should be
|
||||
separated by at least multiple meters to ensure that the UE will receive
|
||||
different signal strengths when moving between cells.
|
||||
|
||||
We only support intra-frequency handovers yet. We have verified with USRPs
|
||||
We support both intra-frequency and inter-frequency handovers. We have verified with USRPs
|
||||
only, although other radios should work as well.
|
||||
|
||||
For UEs, we verified Quectel modules and iPhones. Note, though, that not all
|
||||
|
||||
@@ -381,6 +381,7 @@ typedef struct f1ap_cu_to_du_rrc_info_s {
|
||||
byte_array_t *cg_configinfo;
|
||||
byte_array_t *ue_cap;
|
||||
byte_array_t *meas_config;
|
||||
byte_array_t *meas_timing_config;
|
||||
byte_array_t *ho_prep_info;
|
||||
} f1ap_cu_to_du_rrc_info_t;
|
||||
|
||||
|
||||
@@ -57,8 +57,9 @@ static F1AP_CUtoDURRCInformation_t encode_cu_to_du_rrc_info(const f1ap_cu_to_du_
|
||||
}
|
||||
|
||||
/* optional: HandoverPreparationInformation */
|
||||
F1AP_ProtocolExtensionContainer_10696P60_t *p = NULL;
|
||||
if (cu2du->ho_prep_info) {
|
||||
F1AP_ProtocolExtensionContainer_10696P60_t *p = calloc_or_fail(1, sizeof(*p));
|
||||
p = calloc_or_fail(1, sizeof(*p));
|
||||
enc.iE_Extensions = (struct F1AP_ProtocolExtensionContainer *)p;
|
||||
asn1cSequenceAdd(p->list, F1AP_CUtoDURRCInformation_ExtIEs_t, ie_ext);
|
||||
ie_ext->id = F1AP_ProtocolIE_ID_id_HandoverPreparationInformation;
|
||||
@@ -68,6 +69,20 @@ static F1AP_CUtoDURRCInformation_t encode_cu_to_du_rrc_info(const f1ap_cu_to_du_
|
||||
F1AP_HandoverPreparationInformation_t *hpi = &ie_ext->extensionValue.choice.HandoverPreparationInformation;
|
||||
OCTET_STRING_fromBuf(hpi, (const char *)ba->buf, ba->len);
|
||||
}
|
||||
|
||||
/* optional: iE_Extensions */
|
||||
if (cu2du->meas_timing_config) {
|
||||
if (!p)
|
||||
p = calloc_or_fail(1, sizeof(*p));
|
||||
enc.iE_Extensions = (struct F1AP_ProtocolExtensionContainer *)p;
|
||||
asn1cSequenceAdd(p->list, F1AP_CUtoDURRCInformation_ExtIEs_t, ie_ext);
|
||||
ie_ext->id = F1AP_ProtocolIE_ID_id_MeasurementTimingConfiguration;
|
||||
ie_ext->criticality = F1AP_Criticality_ignore;
|
||||
ie_ext->extensionValue.present = F1AP_CUtoDURRCInformation_ExtIEs__extensionValue_PR_MeasurementTimingConfiguration;
|
||||
const byte_array_t *ba = cu2du->meas_timing_config;
|
||||
F1AP_MeasurementTimingConfiguration_t *mtc = &ie_ext->extensionValue.choice.MeasurementTimingConfiguration;
|
||||
OCTET_STRING_fromBuf(mtc, (const char *)ba->buf, ba->len);
|
||||
}
|
||||
return enc;
|
||||
}
|
||||
|
||||
@@ -98,6 +113,11 @@ static bool decode_cu_to_du_rrc_info(f1ap_cu_to_du_rrc_info_t *dec, const F1AP_C
|
||||
const F1AP_HandoverPreparationInformation_t *hopi = &cu2du_info_ext->extensionValue.choice.HandoverPreparationInformation;
|
||||
*dec->ho_prep_info = create_byte_array(hopi->size, (uint8_t*) hopi->buf);
|
||||
break;
|
||||
case F1AP_ProtocolIE_ID_id_MeasurementTimingConfiguration:
|
||||
dec->meas_timing_config = calloc_or_fail(1, sizeof(*dec->meas_timing_config));
|
||||
const F1AP_MeasurementTimingConfiguration_t *mtc = &cu2du_info_ext->extensionValue.choice.MeasurementTimingConfiguration;
|
||||
*dec->meas_timing_config = create_byte_array(mtc->size, (uint8_t *)mtc->buf);
|
||||
break;
|
||||
default:
|
||||
PRINT_ERROR("received unsupported F1AP CUtoDURRCInfo extension container %ld\n", cu2du_info_ext->id);
|
||||
}
|
||||
@@ -112,6 +132,7 @@ static f1ap_cu_to_du_rrc_info_t cp_cu_to_du_rrc_info(const f1ap_cu_to_du_rrc_inf
|
||||
CP_OPT_BYTE_ARRAY(dst.cg_configinfo, src->cg_configinfo);
|
||||
CP_OPT_BYTE_ARRAY(dst.ue_cap, src->ue_cap);
|
||||
CP_OPT_BYTE_ARRAY(dst.meas_config, src->meas_config);
|
||||
CP_OPT_BYTE_ARRAY(dst.meas_timing_config, src->meas_timing_config);
|
||||
CP_OPT_BYTE_ARRAY(dst.ho_prep_info, src->ho_prep_info);
|
||||
return dst;
|
||||
}
|
||||
@@ -126,6 +147,7 @@ static bool eq_cu_to_du_rrc_info(const f1ap_cu_to_du_rrc_info_t *a, const f1ap_c
|
||||
_F1_EQ_CHECK_OPTIONAL_IE(a, b, cg_configinfo, eq_ba);
|
||||
_F1_EQ_CHECK_OPTIONAL_IE(a, b, ue_cap, eq_ba);
|
||||
_F1_EQ_CHECK_OPTIONAL_IE(a, b, meas_config, eq_ba);
|
||||
_F1_EQ_CHECK_OPTIONAL_IE(a, b, meas_timing_config, eq_ba);
|
||||
_F1_EQ_CHECK_OPTIONAL_IE(a, b, ho_prep_info, eq_ba);
|
||||
return true;
|
||||
}
|
||||
@@ -135,6 +157,7 @@ static void free_cu_to_du_rrc_info(f1ap_cu_to_du_rrc_info_t *cu2du)
|
||||
FREE_OPT_BYTE_ARRAY(cu2du->cg_configinfo);
|
||||
FREE_OPT_BYTE_ARRAY(cu2du->ue_cap);
|
||||
FREE_OPT_BYTE_ARRAY(cu2du->meas_config);
|
||||
FREE_OPT_BYTE_ARRAY(cu2du->meas_timing_config);
|
||||
FREE_OPT_BYTE_ARRAY(cu2du->ho_prep_info);
|
||||
}
|
||||
|
||||
|
||||
@@ -755,6 +755,7 @@ static void test_f1ap_ue_context_setup_request()
|
||||
orig.cu_to_du_rrc_info.cg_configinfo = get_malloced_test_ba("CG-CONFIGINFO");
|
||||
orig.cu_to_du_rrc_info.ue_cap = get_malloced_test_ba("UE CAP");
|
||||
orig.cu_to_du_rrc_info.meas_config = get_malloced_test_ba("UE MEASUREMENT CONFIGURATION");
|
||||
orig.cu_to_du_rrc_info.meas_timing_config = get_malloced_test_ba("MEASUREMENT TIMING CONFIGURATION");
|
||||
orig.cu_to_du_rrc_info.ho_prep_info = get_malloced_test_ba("PREPARATION DATA FOR HANDOVER");
|
||||
orig.srbs_len = 2;
|
||||
orig.srbs = calloc_or_fail(orig.srbs_len, sizeof(*orig.srbs));
|
||||
@@ -996,6 +997,7 @@ static void test_f1ap_ue_context_modification_request()
|
||||
.cg_configinfo = get_malloced_test_ba("Modified"),
|
||||
.ue_cap = get_malloced_test_ba("UE forgot to send earlier"),
|
||||
.meas_config = get_malloced_test_ba("with updates from another cell"),
|
||||
.meas_timing_config = get_malloced_test_ba("measurement timing configuration"),
|
||||
.ho_prep_info = get_malloced_test_ba("prepare to ho soon!"),
|
||||
};
|
||||
_F1_MALLOC(orig.cu_to_du_rrc_info, cu2du);
|
||||
|
||||
@@ -201,6 +201,7 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, frame_t frame, slot_t slo
|
||||
LOG_I(NR_MAC, "Frame.Slot %d.%d\n%s\n", frame, slot, stats_output);
|
||||
}
|
||||
|
||||
nr_measgap_scheduling(gNB, frame, slot);
|
||||
nr_mac_update_timers(module_idP, frame, slot);
|
||||
|
||||
if (wait_prach_completed || get_softmodem_params()->phy_test) {
|
||||
|
||||
@@ -628,6 +628,7 @@ static void pf_dl(module_id_t module_id,
|
||||
NR_sched_pdsch_t *sched_pdsch = &sched_ctrl->sched_pdsch;
|
||||
/* get the PID of a HARQ process awaiting retrnasmission, or -1 otherwise */
|
||||
sched_pdsch->dl_harq_pid = sched_ctrl->retrans_dl_harq.head;
|
||||
|
||||
/* Calculate Throughput */
|
||||
const float a = 0.01f;
|
||||
const uint32_t b = UE->mac_stats.dl.current_bytes;
|
||||
|
||||
@@ -3158,6 +3158,37 @@ void nr_csirs_scheduling(int Mod_idP, frame_t frame, slot_t slot, nfapi_nr_dl_tt
|
||||
}
|
||||
}
|
||||
|
||||
void nr_measgap_scheduling(gNB_MAC_INST *nr_mac, frame_t frame, sub_frame_t slot)
|
||||
{
|
||||
NR_SCHED_ENSURE_LOCKED(&nr_mac->sched_lock);
|
||||
|
||||
NR_UEs_t *UE_info = &nr_mac->UE_info;
|
||||
UE_iterator(UE_info->connected_ue_list, UE) {
|
||||
measgap_config_t *mgc = &UE->measgap_config;
|
||||
if (!mgc->enable)
|
||||
continue;
|
||||
|
||||
const int slots_frame = nr_mac->frame_structure.numb_slots_frame;
|
||||
const frame_t f = (frame + (slot + mgc->n_slots_advance) / slots_frame) % 1024;
|
||||
const slot_t s = (slot + mgc->n_slots_advance) % slots_frame;
|
||||
|
||||
// TS 38 331 - Section 5.5.2.9 Measurement gap configuration
|
||||
if (!(((f % (mgc->mgrp_ms / 10)) == (mgc->gapOffset / 10)) && (s == mgc->gapOffset % 10)))
|
||||
continue;
|
||||
|
||||
NR_timer_t *t = &UE->UE_sched_ctrl.transm_interrupt;
|
||||
interrupt_followup_action_t a = nr_timer_is_active(t) ? UE->interrupt_action : FOLLOW_INSYNC;
|
||||
// start a timer to stop scheduling UE during MeasGap, or extend timer for
|
||||
// duration of measGap with existing follow-up action
|
||||
// TODO: if the timer is running, it might be to stop scheduling of the UE afterwards, but then it does not make sense to stop
|
||||
// scheduling the UE now, we should maybe just skip it?
|
||||
if (!nr_timer_is_active(t) || nr_timer_remaining_time(t) < mgc->mgl_slots) {
|
||||
nr_mac_trigger_ul_failure(&UE->UE_sched_ctrl, UE->current_DL_BWP.scs); /* set the UE to "not active" */
|
||||
nr_mac_interrupt_ue_transmission(nr_mac, UE, a, mgc->mgl_slots);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void nr_mac_clean_cellgroup(NR_CellGroupConfig_t *cell_group)
|
||||
{
|
||||
DevAssert(cell_group != NULL);
|
||||
@@ -3281,8 +3312,12 @@ void nr_mac_update_timers(module_id_t module_id, frame_t frame, slot_t slot)
|
||||
if (nr_timer_tick(&sched_ctrl->transm_interrupt)) {
|
||||
/* expired */
|
||||
nr_timer_stop(&sched_ctrl->transm_interrupt);
|
||||
if (UE->interrupt_action == FOLLOW_OUTOFSYNC)
|
||||
if (UE->interrupt_action == FOLLOW_OUTOFSYNC) {
|
||||
nr_mac_trigger_ul_failure(sched_ctrl, UE->current_DL_BWP.scs);
|
||||
} else {
|
||||
DevAssert(UE->interrupt_action == FOLLOW_INSYNC);
|
||||
nr_mac_reset_ul_failure(sched_ctrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,6 +203,8 @@ void nr_csirs_scheduling(int Mod_idP, frame_t frame, slot_t slot, nfapi_nr_dl_tt
|
||||
|
||||
void nr_csi_meas_reporting(int Mod_idP, frame_t frameP, slot_t slotP);
|
||||
|
||||
void nr_measgap_scheduling(gNB_MAC_INST *nr_mac, frame_t frame, sub_frame_t slot);
|
||||
|
||||
int nr_acknack_scheduling(gNB_MAC_INST *mac,
|
||||
NR_UE_info_t *UE,
|
||||
frame_t frameP,
|
||||
|
||||
@@ -631,7 +631,9 @@ void ue_context_setup_request(const f1ap_ue_context_setup_req_t *req)
|
||||
} else if (cu2du->ue_cap != NULL) {
|
||||
ue_cap = get_ue_nr_cap(*req->gNB_DU_ue_id, cu2du->ue_cap->buf, cu2du->ue_cap->len);
|
||||
}
|
||||
AssertFatal(cu2du->meas_config == NULL, "MeasConfig not handled\n");
|
||||
NR_MeasurementTimingConfiguration_t *mtc = NULL;
|
||||
if (cu2du->meas_timing_config != NULL)
|
||||
mtc = get_nr_mtc(cu2du->meas_timing_config->buf, cu2du->meas_timing_config->len);
|
||||
|
||||
/* 38.473: "For DC operation, the CG-ConfigInfo IE shall be included in the CU
|
||||
* to DU RRC Information IE at the gNB acting as secondary node" As of now,
|
||||
@@ -703,6 +705,16 @@ void ue_context_setup_request(const f1ap_ue_context_setup_req_t *req)
|
||||
int ss_type = cg_configinfo ? NR_SearchSpace__searchSpaceType_PR_ue_Specific: NR_SearchSpace__searchSpaceType_PR_common;
|
||||
configure_UE_BWP(mac, scc, UE, false, ss_type, -1, -1);
|
||||
|
||||
if (mtc) {
|
||||
/* creates a suitable measGap config to be used in the gNB */
|
||||
UE->measgap_config = create_measgap_config(mtc, UE->current_DL_BWP.scs, mac->radio_config.minRXTXTIME);
|
||||
/* encodes the measGapConfig created, if useful (or not!) */
|
||||
byte_array_t *mgc = calloc_or_fail(1, sizeof(*mgc));
|
||||
mgc->buf = calloc_or_fail(1, 1024);
|
||||
mgc->len = encode_measgap_config(&UE->measgap_config, mgc->buf);
|
||||
resp.du_to_cu_rrc_info.meas_gap_config = mgc;
|
||||
}
|
||||
|
||||
NR_SCHED_UNLOCK(&mac->sched_lock);
|
||||
|
||||
mac->mac_rrc.ue_context_setup_response(&resp);
|
||||
@@ -727,7 +739,6 @@ void ue_context_modification_request(const f1ap_ue_context_mod_req_t *req)
|
||||
byte_array_t *b = req->cu_to_du_rrc_info->ue_cap;
|
||||
ue_cap = get_ue_nr_cap(req->gNB_DU_ue_id, b->buf, b->len);
|
||||
}
|
||||
AssertFatal(req->cu_to_du_rrc_info->meas_config == NULL, "MeasConfig not handled\n");
|
||||
}
|
||||
|
||||
NR_SCHED_LOCK(&mac->sched_lock);
|
||||
@@ -957,6 +968,7 @@ void dl_rrc_message_transfer(const f1ap_dl_rrc_message_t *dl_rrc)
|
||||
UE->CellGroup = oldUE->CellGroup;
|
||||
oldUE->CellGroup = NULL;
|
||||
UE->mac_stats = oldUE->mac_stats;
|
||||
UE->measgap_config = oldUE->measgap_config;
|
||||
/* 38.331 5.3.7.2 says that the UE releases the spCellConfig, so we drop it
|
||||
* from the current configuration. It will be reapplied when the
|
||||
* reconfiguration has succeeded (indicated by the CU) */
|
||||
|
||||
@@ -93,7 +93,6 @@ static void ue_context_modification_required_direct(const f1ap_ue_context_modif_
|
||||
du2cu->cellGroupConfig = malloc(du2cu->cellGroupConfig_length * sizeof(*du2cu->cellGroupConfig));
|
||||
AssertFatal(du2cu->cellGroupConfig != NULL, "out of memory\n");
|
||||
memcpy(du2cu->cellGroupConfig, required->du_to_cu_rrc_information->cellGroupConfig, du2cu->cellGroupConfig_length);
|
||||
AssertFatal(required->du_to_cu_rrc_information->measGapConfig == NULL && required->du_to_cu_rrc_information->measGapConfig_length == 0, "not handled yet\n");
|
||||
AssertFatal(required->du_to_cu_rrc_information->requestedP_MaxFR1 == NULL && required->du_to_cu_rrc_information->requestedP_MaxFR1_length == 0, "not handled yet\n");
|
||||
}
|
||||
f1ap_msg->cause = required->cause;
|
||||
|
||||
@@ -117,7 +117,6 @@ static void ue_context_modification_required_f1ap(const f1ap_ue_context_modif_re
|
||||
du2cu->cellGroupConfig = malloc(du2cu->cellGroupConfig_length * sizeof(*du2cu->cellGroupConfig));
|
||||
AssertFatal(du2cu->cellGroupConfig != NULL, "out of memory\n");
|
||||
memcpy(du2cu->cellGroupConfig, required->du_to_cu_rrc_information->cellGroupConfig, du2cu->cellGroupConfig_length);
|
||||
AssertFatal(required->du_to_cu_rrc_information->measGapConfig == NULL && required->du_to_cu_rrc_information->measGapConfig_length == 0, "not handled yet\n");
|
||||
AssertFatal(required->du_to_cu_rrc_information->requestedP_MaxFR1 == NULL && required->du_to_cu_rrc_information->requestedP_MaxFR1_length == 0, "not handled yet\n");
|
||||
}
|
||||
f1ap_msg->cause = required->cause;
|
||||
|
||||
@@ -733,7 +733,20 @@ typedef struct nr_mac_rrc_ul_if_s {
|
||||
initial_ul_rrc_message_transfer_func_t initial_ul_rrc_message_transfer;
|
||||
} nr_mac_rrc_ul_if_t;
|
||||
|
||||
typedef enum interrupt_followup_action { FOLLOW_INSYNC, FOLLOW_INSYNC_RECONFIG, FOLLOW_OUTOFSYNC } interrupt_followup_action_t;
|
||||
typedef struct measgap_config {
|
||||
bool enable;
|
||||
int mgrp_ms;
|
||||
long mgrp;
|
||||
long gapOffset;
|
||||
long mgta;
|
||||
int n_slots_mgta;
|
||||
int n_slots_advance;
|
||||
float mgl_ms;
|
||||
long mgl;
|
||||
int mgl_slots;
|
||||
} measgap_config_t;
|
||||
|
||||
typedef enum interrupt_followup_action { FOLLOW_INSYNC, FOLLOW_OUTOFSYNC } interrupt_followup_action_t;
|
||||
|
||||
/*! \brief UE list used by gNB to order UEs/CC for scheduling*/
|
||||
typedef struct {
|
||||
@@ -753,6 +766,7 @@ typedef struct {
|
||||
NR_SpCellConfig_t *reconfigSpCellConfig;
|
||||
interrupt_followup_action_t interrupt_action;
|
||||
NR_UE_NR_Capability_t *capability;
|
||||
measgap_config_t measgap_config;
|
||||
// UE selected beam index
|
||||
uint8_t UE_beam_index;
|
||||
float ul_thr_ue;
|
||||
|
||||
@@ -4090,3 +4090,198 @@ NR_ReconfigurationWithSync_t *get_reconfiguration_with_sync(rnti_t rnti, uid_t u
|
||||
|
||||
return reconfigurationWithSync;
|
||||
}
|
||||
|
||||
static NR_MeasGapConfig_t *get_gap_config_from_smtc(const NR_SSB_MTC_t *ssb_mtc)
|
||||
{
|
||||
NR_MeasGapConfig_t *measGapConfig = calloc_or_fail(1, sizeof(*measGapConfig));
|
||||
measGapConfig->ext1 = calloc_or_fail(1, sizeof(*measGapConfig->ext1));
|
||||
measGapConfig->ext1->gapUE = calloc_or_fail(1, sizeof(*measGapConfig->ext1->gapUE));
|
||||
measGapConfig->ext1->gapUE->present = NR_SetupRelease_GapConfig_PR_setup;
|
||||
NR_GapConfig_t *gap_config = calloc_or_fail(1, sizeof(*gap_config));
|
||||
measGapConfig->ext1->gapUE->choice.setup = gap_config;
|
||||
|
||||
// mgta = Measurement Gap Timing Advance, to provide sufficient time for the UE to re-tune its transceiver
|
||||
// This allows the Measurement Gap to extend mgta ms either side of the SS/PBCH Measurement Window.
|
||||
gap_config->mgta = NR_GapConfig__mgta_ms0dot5;
|
||||
|
||||
// mgrp = Measurement Gap Repetition Period
|
||||
// gapOffset = It defines the start of the Measurement Gaps relative to the start of the radio frame with SFN = 0
|
||||
// The Measurement Gaps need to be synchronized with the SS/PBCH transmissions which are to be measured
|
||||
gap_config->mgrp = NR_GapConfig__mgrp_ms160;
|
||||
switch (ssb_mtc->periodicityAndOffset.present) {
|
||||
case NR_SSB_MTC__periodicityAndOffset_PR_sf20:
|
||||
gap_config->gapOffset = ssb_mtc->periodicityAndOffset.choice.sf20;
|
||||
break;
|
||||
case NR_SSB_MTC__periodicityAndOffset_PR_sf40:
|
||||
gap_config->gapOffset = ssb_mtc->periodicityAndOffset.choice.sf40;
|
||||
break;
|
||||
case NR_SSB_MTC__periodicityAndOffset_PR_sf80:
|
||||
gap_config->gapOffset = ssb_mtc->periodicityAndOffset.choice.sf80;
|
||||
break;
|
||||
case NR_SSB_MTC__periodicityAndOffset_PR_sf160:
|
||||
gap_config->gapOffset = ssb_mtc->periodicityAndOffset.choice.sf160;
|
||||
break;
|
||||
default:
|
||||
LOG_W(NR_RRC, "SMTC periodicity higher than MGRP\n");
|
||||
// With this configuration, not all SSBs belong to the Measurement Gap.
|
||||
if (ssb_mtc->periodicityAndOffset.present == NR_SSB_MTC__periodicityAndOffset_PR_sf5) {
|
||||
gap_config->gapOffset = ssb_mtc->periodicityAndOffset.choice.sf5;
|
||||
} else if (ssb_mtc->periodicityAndOffset.present == NR_SSB_MTC__periodicityAndOffset_PR_sf10) {
|
||||
gap_config->gapOffset = ssb_mtc->periodicityAndOffset.choice.sf10;
|
||||
}
|
||||
}
|
||||
|
||||
// mgl = Measurement Gap Length
|
||||
// FIXME: At least the duration of the SMTC plus 2 times the mgta should be enough,
|
||||
// however, at the moment it only works by setting the maximum value
|
||||
gap_config->mgl = NR_GapConfig__mgl_ms6;
|
||||
|
||||
return measGapConfig;
|
||||
}
|
||||
|
||||
NR_MeasurementTimingConfiguration_t *get_nr_mtc(uint8_t *buf, uint32_t len)
|
||||
{
|
||||
if (buf == NULL || len == 0)
|
||||
return NULL;
|
||||
|
||||
NR_MeasurementTimingConfiguration_t *mtc = NULL;
|
||||
asn_dec_rval_t dec_rval = uper_decode(NULL, &asn_DEF_NR_MeasurementTimingConfiguration, (void **)&mtc, buf, len, 0, 0);
|
||||
if (dec_rval.code != RC_OK) {
|
||||
LOG_E(NR_MAC, "cannot decode NR MeasurementTimingConfiguration, ignoring\n");
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_MeasurementTimingConfiguration, mtc);
|
||||
return NULL;
|
||||
}
|
||||
return mtc;
|
||||
}
|
||||
|
||||
/** @brief return Measurement Gap Repetition Period from ASN.1 config */
|
||||
static int get_mgrp(long mgrp)
|
||||
{
|
||||
switch (mgrp) {
|
||||
case NR_GapConfig__mgrp_ms20:
|
||||
return 20;
|
||||
case NR_GapConfig__mgrp_ms40:
|
||||
return 40;
|
||||
case NR_GapConfig__mgrp_ms80:
|
||||
return 80;
|
||||
case NR_GapConfig__mgrp_ms160:
|
||||
return 160;
|
||||
default:
|
||||
LOG_E(NR_MAC, "Invalid MGRP %ld\n", mgrp);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Return Measurement Gap Length from ASN.1 config */
|
||||
static float get_mgl(long mgl)
|
||||
{
|
||||
switch (mgl) {
|
||||
case NR_GapConfig__mgl_ms1dot5:
|
||||
return 1.5;
|
||||
case NR_GapConfig__mgl_ms3:
|
||||
return 3.0;
|
||||
case NR_GapConfig__mgl_ms3dot5:
|
||||
return 3.5;
|
||||
case NR_GapConfig__mgl_ms4:
|
||||
return 4;
|
||||
case NR_GapConfig__mgl_ms5dot5:
|
||||
return 5.5;
|
||||
case NR_GapConfig__mgl_ms6:
|
||||
return 6;
|
||||
default:
|
||||
LOG_E(NR_MAC, "Invalid MGL %ld\n", mgl);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Return Measurement Gap Timing Advance, from ASN.1 config */
|
||||
static float get_mgta(long mgta)
|
||||
{
|
||||
switch (mgta) {
|
||||
case NR_GapConfig__mgta_ms0:
|
||||
return 0.0;
|
||||
case NR_GapConfig__mgta_ms0dot25:
|
||||
return 0.25;
|
||||
case NR_GapConfig__mgta_ms0dot5:
|
||||
return 0.5;
|
||||
default:
|
||||
LOG_E(NR_MAC, "Invalid MGTA %ld\n", mgta);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Return Measurement Gap Configuration, from ASN.1 config */
|
||||
const NR_GapConfig_t *get_gap_config(const NR_MeasGapConfig_t *mgc)
|
||||
{
|
||||
if (mgc->gapFR2 && mgc->gapFR2->present == NR_SetupRelease_GapConfig_PR_setup)
|
||||
return mgc->gapFR2->choice.setup;
|
||||
|
||||
// FR1 case
|
||||
if (!mgc->ext1)
|
||||
return NULL;
|
||||
|
||||
const NR_SetupRelease_GapConfig_t *gapUE = mgc->ext1->gapUE;
|
||||
if (gapUE && gapUE->present == NR_SetupRelease_GapConfig_PR_setup)
|
||||
return gapUE->choice.setup;
|
||||
|
||||
const NR_SetupRelease_GapConfig_t *gapFR1 = mgc->ext1->gapFR1;
|
||||
if (gapFR1 && gapFR1->present == NR_SetupRelease_GapConfig_PR_setup)
|
||||
return gapFR1->choice.setup;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
measgap_config_t create_measgap_config(const NR_MeasurementTimingConfiguration_t *mtc, int scs, int min_rxtxtime)
|
||||
{
|
||||
measgap_config_t mgc = {0};
|
||||
const NR_MeasTimingList_t *mtlist = mtc->criticalExtensions.choice.c1->choice.measTimingConf->measTiming;
|
||||
const NR_MeasTiming_t *mt = mtlist->list.array[0];
|
||||
DevAssert(mt != NULL && mt->frequencyAndTiming != NULL);
|
||||
const struct NR_MeasTiming__frequencyAndTiming *ft = mt->frequencyAndTiming;
|
||||
const NR_SSB_MTC_t *ssb_mtc = &ft->ssb_MeasurementTimingConfiguration;
|
||||
NR_MeasGapConfig_t *measGapConfig = get_gap_config_from_smtc(ssb_mtc);
|
||||
const NR_GapConfig_t *gap_config = get_gap_config(measGapConfig);
|
||||
if (!gap_config)
|
||||
return mgc;
|
||||
|
||||
mgc.mgrp_ms = get_mgrp(gap_config->mgrp);
|
||||
DevAssert(mgc.mgrp_ms != -1);
|
||||
mgc.mgrp = gap_config->mgrp;
|
||||
|
||||
mgc.gapOffset = gap_config->gapOffset;
|
||||
mgc.mgta = gap_config->mgta;
|
||||
mgc.n_slots_mgta = ((int)(10 * get_mgta(gap_config->mgta)) << scs) / 10;
|
||||
|
||||
// We start the timer K2 slots earlier to avoid scheduling feedback PUCCHs inside measGap
|
||||
// or depending on the current min_rxtxtime earlier
|
||||
// TS 38.214 - Table 6.1.2.1.1.2
|
||||
const int max_k2 = max(3, min_rxtxtime);
|
||||
|
||||
mgc.n_slots_advance = mgc.n_slots_mgta + max_k2;
|
||||
|
||||
mgc.mgl_ms = get_mgl(gap_config->mgl);
|
||||
DevAssert(mgc.mgl_ms != -1);
|
||||
mgc.mgl = gap_config->mgl;
|
||||
mgc.mgl_slots = ((int)(10 * (mgc.mgl_ms + max_k2)) << scs) / 10;
|
||||
|
||||
mgc.enable = true;
|
||||
return mgc;
|
||||
}
|
||||
|
||||
int encode_measgap_config(const measgap_config_t *c, uint8_t *buf)
|
||||
{
|
||||
NR_MeasGapConfig_t *measGapConfig = calloc_or_fail(1, sizeof(*measGapConfig));
|
||||
measGapConfig->ext1 = calloc_or_fail(1, sizeof(*measGapConfig->ext1));
|
||||
measGapConfig->ext1->gapUE = calloc_or_fail(1, sizeof(*measGapConfig->ext1->gapUE));
|
||||
measGapConfig->ext1->gapUE->present = NR_SetupRelease_GapConfig_PR_setup;
|
||||
NR_GapConfig_t *gap_config = calloc_or_fail(1, sizeof(*gap_config));
|
||||
measGapConfig->ext1->gapUE->choice.setup = gap_config;
|
||||
gap_config->mgta = c->mgta;
|
||||
gap_config->mgrp = c->mgrp;
|
||||
gap_config->gapOffset = c->gapOffset;
|
||||
gap_config->mgl = c->mgl;
|
||||
asn_enc_rval_t enc_rval_mgc = uper_encode_to_buffer(&asn_DEF_NR_MeasGapConfig, NULL, measGapConfig, buf, 1024);
|
||||
AssertFatal(enc_rval_mgc.encoded > 0, "Could not encode CellGroup, failed element %s\n", enc_rval_mgc.failed_type->name);
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_MeasGapConfig, measGapConfig);
|
||||
return (int)((enc_rval_mgc.encoded + 7) >> 3);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#include "NR_BCCH-DL-SCH-Message.h"
|
||||
#include "NR_CellGroupConfig.h"
|
||||
#include "NR_UE-NR-Capability.h"
|
||||
#include "NR_MeasConfig.h"
|
||||
#include "NR_MeasurementTimingConfiguration.h"
|
||||
#include "NR_UL-CCCH-Message.h"
|
||||
#include "f1ap_messages_types.h"
|
||||
#include "common/platform_types.h"
|
||||
@@ -42,6 +44,9 @@ typedef struct nr_mac_config_t nr_mac_config_t;
|
||||
struct nr_mac_timers;
|
||||
typedef struct nr_mac_timers nr_mac_timers_t;
|
||||
|
||||
struct measgap_config;
|
||||
typedef struct measgap_config measgap_config_t;
|
||||
|
||||
void nr_rrc_config_dl_tda(struct NR_PDSCH_TimeDomainResourceAllocationList *pdsch_TimeDomainAllocationList,
|
||||
frame_type_t frame_type,
|
||||
NR_TDD_UL_DL_ConfigCommon_t *tdd_UL_DL_ConfigurationCommon,
|
||||
@@ -119,4 +124,8 @@ NR_RLC_BearerConfig_t *get_DRB_RLC_BearerConfig(long lcChannelId,
|
||||
long priority,
|
||||
const nr_rlc_configuration_t *default_rlc_config);
|
||||
|
||||
NR_MeasurementTimingConfiguration_t *get_nr_mtc(uint8_t *buf, uint32_t len);
|
||||
measgap_config_t create_measgap_config(const NR_MeasurementTimingConfiguration_t *mtc, int scs, int min_rxtxtime);
|
||||
int encode_measgap_config(const measgap_config_t *c, uint8_t *buf);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -956,23 +956,59 @@ int do_RRCReestablishmentComplete(uint8_t *buffer, size_t buffer_size, int64_t r
|
||||
return((enc_rval.encoded+7)/8);
|
||||
}
|
||||
|
||||
static NR_MeasObjectToAddMod_t *get_MeasObject(const struct NR_MeasTiming__frequencyAndTiming *ft,
|
||||
int band,
|
||||
NR_ARFCN_ValueNR_t ssbFrequency,
|
||||
NR_MeasObjectId_t measObjectId)
|
||||
{
|
||||
const NR_SSB_MTC_t *ssb_mtc = &ft->ssb_MeasurementTimingConfiguration;
|
||||
NR_MeasObjectToAddMod_t *mo = calloc_or_fail(1, sizeof(*mo));
|
||||
mo->measObjectId = measObjectId;
|
||||
mo->measObject.present = NR_MeasObjectToAddMod__measObject_PR_measObjectNR;
|
||||
NR_MeasObjectNR_t *monr = calloc_or_fail(1, sizeof(*monr));
|
||||
asn1cCallocOne(monr->ssbFrequency, ssbFrequency);
|
||||
asn1cCallocOne(monr->ssbSubcarrierSpacing, ft->ssbSubcarrierSpacing);
|
||||
monr->referenceSignalConfig.ssb_ConfigMobility = calloc_or_fail(1, sizeof(*monr->referenceSignalConfig.ssb_ConfigMobility));
|
||||
monr->referenceSignalConfig.ssb_ConfigMobility->deriveSSB_IndexFromCell = true;
|
||||
monr->absThreshSS_BlocksConsolidation = calloc_or_fail(1, sizeof(*monr->absThreshSS_BlocksConsolidation));
|
||||
asn1cCallocOne(monr->absThreshSS_BlocksConsolidation->thresholdRSRP, 36);
|
||||
asn1cCallocOne(monr->nrofSS_BlocksToAverage, 8);
|
||||
monr->smtc1 = calloc_or_fail(1, sizeof(*monr->smtc1));
|
||||
monr->smtc1->periodicityAndOffset = ssb_mtc->periodicityAndOffset;
|
||||
monr->smtc1->duration = ssb_mtc->duration;
|
||||
monr->quantityConfigIndex = 1;
|
||||
monr->ext1 = calloc_or_fail(1, sizeof(*monr->ext1));
|
||||
asn1cCallocOne(monr->ext1->freqBandIndicatorNR, band);
|
||||
mo->measObject.choice.measObjectNR = monr;
|
||||
return mo;
|
||||
}
|
||||
|
||||
static NR_MeasIdToAddMod_t *get_MeasId(NR_MeasId_t measId, NR_ReportConfigId_t reportConfigId, NR_MeasObjectId_t measObjectId)
|
||||
{
|
||||
NR_MeasIdToAddMod_t *measid = calloc_or_fail(1, sizeof(NR_MeasIdToAddMod_t));
|
||||
measid->measId = measId;
|
||||
measid->reportConfigId = reportConfigId;
|
||||
measid->measObjectId = measObjectId;
|
||||
return measid;
|
||||
}
|
||||
|
||||
NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt,
|
||||
int band,
|
||||
int scs,
|
||||
NR_ReportConfigToAddMod_t *rc_PER,
|
||||
NR_ReportConfigToAddMod_t *rc_A2,
|
||||
seq_arr_t *rc_A3_seq,
|
||||
seq_arr_t *pci_seq)
|
||||
seq_arr_t *neigh_seq)
|
||||
{
|
||||
NR_MeasConfig_t *mc = calloc(1, sizeof(*mc));
|
||||
mc->measObjectToAddModList = calloc(1, sizeof(*mc->measObjectToAddModList));
|
||||
mc->reportConfigToAddModList = calloc(1, sizeof(*mc->reportConfigToAddModList));
|
||||
mc->measIdToAddModList = calloc(1, sizeof(*mc->measIdToAddModList));
|
||||
NR_MeasConfig_t *mc = calloc_or_fail(1, sizeof(*mc));
|
||||
mc->measObjectToAddModList = calloc_or_fail(1, sizeof(*mc->measObjectToAddModList));
|
||||
mc->reportConfigToAddModList = calloc_or_fail(1, sizeof(*mc->reportConfigToAddModList));
|
||||
mc->measIdToAddModList = calloc_or_fail(1, sizeof(*mc->measIdToAddModList));
|
||||
|
||||
if (rc_PER)
|
||||
asn1cSeqAdd(&mc->reportConfigToAddModList->list, rc_PER);
|
||||
|
||||
if (rc_A2)
|
||||
if (rc_A2)
|
||||
asn1cSeqAdd(&mc->reportConfigToAddModList->list, rc_A2);
|
||||
|
||||
if (rc_A3_seq) {
|
||||
@@ -984,57 +1020,51 @@ NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt,
|
||||
|
||||
DevAssert(mt != NULL && mt->frequencyAndTiming != NULL);
|
||||
const struct NR_MeasTiming__frequencyAndTiming *ft = mt->frequencyAndTiming;
|
||||
const NR_SSB_MTC_t *ssb_mtc = &ft->ssb_MeasurementTimingConfiguration;
|
||||
|
||||
// Measurement Objects: Specifies what is to be measured. For NR and inter-RAT E-UTRA measurements, this may include
|
||||
// cell-specific offsets, blacklisted cells to be ignored and whitelisted cells to consider for measurements.
|
||||
NR_MeasObjectToAddMod_t *mo1 = calloc(1, sizeof(*mo1));
|
||||
mo1->measObjectId = 1;
|
||||
mo1->measObject.present = NR_MeasObjectToAddMod__measObject_PR_measObjectNR;
|
||||
NR_MeasObjectNR_t *monr1 = calloc(1, sizeof(*monr1));
|
||||
asn1cCallocOne(monr1->ssbFrequency, ft->carrierFreq);
|
||||
asn1cCallocOne(monr1->ssbSubcarrierSpacing, ft->ssbSubcarrierSpacing);
|
||||
monr1->referenceSignalConfig.ssb_ConfigMobility = calloc(1, sizeof(*monr1->referenceSignalConfig.ssb_ConfigMobility));
|
||||
monr1->referenceSignalConfig.ssb_ConfigMobility->deriveSSB_IndexFromCell = true;
|
||||
monr1->absThreshSS_BlocksConsolidation = calloc(1, sizeof(*monr1->absThreshSS_BlocksConsolidation));
|
||||
asn1cCallocOne(monr1->absThreshSS_BlocksConsolidation->thresholdRSRP, 36);
|
||||
asn1cCallocOne(monr1->nrofSS_BlocksToAverage, 8);
|
||||
monr1->smtc1 = calloc(1, sizeof(*monr1->smtc1));
|
||||
monr1->smtc1->periodicityAndOffset = ssb_mtc->periodicityAndOffset;
|
||||
monr1->smtc1->duration = ssb_mtc->duration;
|
||||
monr1->quantityConfigIndex = 1;
|
||||
monr1->ext1 = calloc(1, sizeof(*monr1->ext1));
|
||||
asn1cCallocOne(monr1->ext1->freqBandIndicatorNR, band);
|
||||
|
||||
if (pci_seq) {
|
||||
for (int i = 0; i < pci_seq->size; i++) {
|
||||
if (monr1->cellsToAddModList == NULL) {
|
||||
monr1->cellsToAddModList = calloc(1, sizeof(*monr1->cellsToAddModList));
|
||||
}
|
||||
|
||||
NR_CellsToAddMod_t *cell = calloc(1, sizeof(*cell));
|
||||
int *pci = (int *)seq_arr_at(pci_seq, i);
|
||||
cell->physCellId = *pci;
|
||||
NR_MeasObjectToAddMod_t *mo1 = get_MeasObject(ft, band, ft->carrierFreq, 1);
|
||||
if (neigh_seq) {
|
||||
NR_MeasObjectNR_t *monr1 = mo1->measObject.choice.measObjectNR;
|
||||
if (monr1->cellsToAddModList == NULL)
|
||||
monr1->cellsToAddModList = calloc_or_fail(1, sizeof(*monr1->cellsToAddModList));
|
||||
FOR_EACH_SEQ_ARR(nr_neighbour_cell_t *, n, neigh_seq) {
|
||||
NR_CellsToAddMod_t *cell = calloc_or_fail(1, sizeof(*cell));
|
||||
cell->physCellId = n->physicalCellId;
|
||||
ASN_SEQUENCE_ADD(&monr1->cellsToAddModList->list, cell);
|
||||
}
|
||||
}
|
||||
|
||||
mo1->measObject.choice.measObjectNR = monr1;
|
||||
asn1cSeqAdd(&mc->measObjectToAddModList->list, mo1);
|
||||
|
||||
// Preparation of measId
|
||||
for (uint8_t reportIdx = 0; reportIdx < mc->reportConfigToAddModList->list.count; reportIdx++) {
|
||||
uint8_t reportIdx = 0;
|
||||
for (; reportIdx < mc->reportConfigToAddModList->list.count; reportIdx++) {
|
||||
const NR_ReportConfigId_t reportId = mc->reportConfigToAddModList->list.array[reportIdx]->reportConfigId;
|
||||
NR_MeasIdToAddMod_t *measid = calloc(1, sizeof(NR_MeasIdToAddMod_t));
|
||||
measid->measId = reportIdx + 1;
|
||||
measid->reportConfigId = reportId;
|
||||
measid->measObjectId = 1;
|
||||
NR_MeasIdToAddMod_t *measid = get_MeasId(reportIdx + 1, reportId, 1);
|
||||
asn1cSeqAdd(&mc->measIdToAddModList->list, measid);
|
||||
}
|
||||
|
||||
mc->quantityConfig = calloc(1, sizeof(*mc->quantityConfig));
|
||||
mc->quantityConfig->quantityConfigNR_List = calloc(1, sizeof(*mc->quantityConfig->quantityConfigNR_List));
|
||||
NR_QuantityConfigNR_t *qcnr = calloc(1, sizeof(*qcnr));
|
||||
// Preparation of measId for neighbour cells for periodic report
|
||||
if (neigh_seq) {
|
||||
int mo_id = 2;
|
||||
FOR_EACH_SEQ_ARR(nr_neighbour_cell_t *, neigh_cell, neigh_seq) {
|
||||
NR_MeasObjectToAddMod_t *mo_neighbour = get_MeasObject(ft, band, neigh_cell->absoluteFrequencySSB, mo_id);
|
||||
NR_MeasObjectNR_t *monr = mo_neighbour->measObject.choice.measObjectNR;
|
||||
monr->cellsToAddModList = calloc_or_fail(1, sizeof(*monr->cellsToAddModList));
|
||||
NR_CellsToAddMod_t *cell = calloc_or_fail(1, sizeof(*cell));
|
||||
cell->physCellId = neigh_cell->physicalCellId;
|
||||
ASN_SEQUENCE_ADD(&monr->cellsToAddModList->list, cell);
|
||||
asn1cSeqAdd(&mc->measObjectToAddModList->list, mo_neighbour);
|
||||
NR_MeasIdToAddMod_t *measid = get_MeasId(reportIdx + 1, rc_PER->reportConfigId, mo_id);
|
||||
asn1cSeqAdd(&mc->measIdToAddModList->list, measid);
|
||||
reportIdx++;
|
||||
mo_id++;
|
||||
}
|
||||
}
|
||||
|
||||
mc->quantityConfig = calloc_or_fail(1, sizeof(*mc->quantityConfig));
|
||||
mc->quantityConfig->quantityConfigNR_List = calloc_or_fail(1, sizeof(*mc->quantityConfig->quantityConfigNR_List));
|
||||
NR_QuantityConfigNR_t *qcnr = calloc_or_fail(1, sizeof(*qcnr));
|
||||
asn1cCallocOne(qcnr->quantityConfigCell.ssb_FilterConfig.filterCoefficientRSRP, NR_FilterCoefficient_fc6);
|
||||
asn1cCallocOne(qcnr->quantityConfigCell.csi_RS_FilterConfig.filterCoefficientRSRP, NR_FilterCoefficient_fc6);
|
||||
asn1cSeqAdd(&mc->quantityConfig->quantityConfigNR_List->list, qcnr);
|
||||
@@ -1122,3 +1152,17 @@ int do_NR_HandoverPreparationInformation(const uint8_t *uecap_buf, int uecap_buf
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_HandoverPreparationInformation, hpi);
|
||||
return (enc_rval.encoded + 7) / 8;
|
||||
}
|
||||
|
||||
int do_NR_MeasConfig(const NR_MeasConfig_t *measconfig, uint8_t *buf, int buf_size)
|
||||
{
|
||||
asn_enc_rval_t enc_rval = uper_encode_to_buffer(&asn_DEF_NR_MeasConfig, NULL, measconfig, buf, buf_size);
|
||||
AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", enc_rval.failed_type->name, enc_rval.encoded);
|
||||
return (enc_rval.encoded + 7) / 8;
|
||||
}
|
||||
|
||||
int do_NR_MeasurementTimingConfiguration(const NR_MeasurementTimingConfiguration_t *mtc, uint8_t *buf, int buf_size)
|
||||
{
|
||||
asn_enc_rval_t enc_rval = uper_encode_to_buffer(&asn_DEF_NR_MeasurementTimingConfiguration, NULL, mtc, buf, buf_size);
|
||||
AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", enc_rval.failed_type->name, enc_rval.encoded);
|
||||
return (enc_rval.encoded + 7) / 8;
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
#include "NR_ReestablishmentCause.h"
|
||||
#include "NR_SRB-ToAddModList.h"
|
||||
#include "NR_SecurityConfig.h"
|
||||
#include "NR_MeasurementTimingConfiguration.h"
|
||||
#include "ds/seq_arr.h"
|
||||
#include "ds/byte_array.h"
|
||||
#include "rrc_messages_types.h"
|
||||
@@ -120,6 +121,10 @@ int do_RRCSetupComplete(uint8_t *buffer,
|
||||
|
||||
int do_NR_HandoverPreparationInformation(const uint8_t *uecap_buf, int uecap_buf_size, uint8_t *buf, int buf_size);
|
||||
|
||||
int do_NR_MeasConfig(const NR_MeasConfig_t *measconfig, uint8_t *buf, int buf_size);
|
||||
|
||||
int do_NR_MeasurementTimingConfiguration(const NR_MeasurementTimingConfiguration_t *mtc, uint8_t *buf, int buf_size);
|
||||
|
||||
int do_RRCSetupRequest(uint8_t *buffer, size_t buffer_size, uint8_t *rv, uint64_t fiveG_S_TMSI_part1);
|
||||
|
||||
int do_NR_RRCReconfigurationComplete_for_nsa(uint8_t *buffer, size_t buffer_size, NR_RRC_TransactionIdentifier_t Transaction_id);
|
||||
@@ -151,7 +156,7 @@ NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt,
|
||||
NR_ReportConfigToAddMod_t *rc_PER,
|
||||
NR_ReportConfigToAddMod_t *rc_A2,
|
||||
seq_arr_t *rc_A3_seq,
|
||||
seq_arr_t *pci_seq);
|
||||
seq_arr_t *neigh_seq);
|
||||
void free_MeasConfig(NR_MeasConfig_t *mc);
|
||||
int do_NR_Paging(uint8_t Mod_id, uint8_t *buffer, uint32_t tmsi);
|
||||
|
||||
|
||||
@@ -72,6 +72,8 @@ void rrc_gNB_generate_dedicatedRRCReconfiguration_release(gNB_RRC_INST *rrc,
|
||||
uint32_t nas_length,
|
||||
uint8_t *nas_buffer);
|
||||
|
||||
NR_MeasConfig_t *nr_rrc_get_measconfig(const gNB_RRC_INST *rrc, uint64_t nr_cellid);
|
||||
|
||||
bool ue_associated_to_cuup(const gNB_RRC_INST *rrc, const gNB_RRC_UE_t *ue);
|
||||
sctp_assoc_t get_existing_cuup_for_ue(const gNB_RRC_INST *rrc, const gNB_RRC_UE_t *ue);
|
||||
sctp_assoc_t get_new_cuup_for_ue(const gNB_RRC_INST *rrc, const gNB_RRC_UE_t *ue, int sst, int sd);
|
||||
|
||||
+46
-21
@@ -585,18 +585,14 @@ void free_RRCReconfiguration_params(nr_rrc_reconfig_param_t params)
|
||||
FREE_AND_ZERO_BYTE_ARRAY(params.dedicated_NAS_msg_list[i]);
|
||||
}
|
||||
|
||||
/** @brief Prepare the instance of RRCReconfigurationParams to be pass to RRC encoding */
|
||||
nr_rrc_reconfig_param_t get_RRCReconfiguration_params(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, uint8_t srb_reest_bitmap, bool drb_reestablish)
|
||||
NR_MeasConfig_t *nr_rrc_get_measconfig(const gNB_RRC_INST *rrc, uint64_t nr_cellid)
|
||||
{
|
||||
uint8_t xid = rrc_gNB_get_next_transaction_identifier(rrc->module_id);
|
||||
nr_rrc_du_container_t *du = get_du_for_ue(rrc, UE->rrc_ue_id);
|
||||
nr_rrc_du_container_t *du = get_du_by_cell_id((gNB_RRC_INST *)rrc, nr_cellid);
|
||||
DevAssert(du != NULL);
|
||||
f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info;
|
||||
NR_MeasConfig_t *measconfig = NULL;
|
||||
NR_ReportConfigToAddMod_t *rc_PER = NULL;
|
||||
NR_ReportConfigToAddMod_t *rc_A2 = NULL;
|
||||
seq_arr_t *rc_A3_seq = NULL;
|
||||
seq_arr_t *pci_seq = NULL;
|
||||
if (du->mtc != NULL) {
|
||||
int scs = get_ssb_scs(cell_info);
|
||||
int band = get_dl_band(cell_info);
|
||||
@@ -607,25 +603,18 @@ nr_rrc_reconfig_param_t get_RRCReconfiguration_params(gNB_RRC_INST *rrc, gNB_RRC
|
||||
if (neighbour_config)
|
||||
neighbour_cells = neighbour_config->neighbour_cells;
|
||||
|
||||
if (neighbour_cells
|
||||
&& rrc->measurementConfiguration.a3_event_list
|
||||
&& rrc->measurementConfiguration.a3_event_list->size > 0) {
|
||||
if (neighbour_cells && rrc->measurementConfiguration.a3_event_list && rrc->measurementConfiguration.a3_event_list->size > 0) {
|
||||
/* Loop through neighbours and find related A3 configuration
|
||||
If no related A3 but there is default add the default one.
|
||||
If default one added once as a report, no need to add it again && duplication.
|
||||
*/
|
||||
rc_A3_seq = malloc(sizeof(seq_arr_t));
|
||||
pci_seq = malloc(sizeof(seq_arr_t));
|
||||
seq_arr_init(rc_A3_seq, sizeof(NR_ReportConfigToAddMod_t));
|
||||
seq_arr_init(pci_seq, sizeof(int));
|
||||
LOG_D(NR_RRC, "HO LOG: Preparing A3 Event Measurement Configuration!\n");
|
||||
bool is_default_a3_added = false;
|
||||
for (int i = 0; i < neighbour_cells->size; i++) {
|
||||
nr_neighbour_cell_t *neighbourCell = (nr_neighbour_cell_t *)seq_arr_at(neighbour_cells, i);
|
||||
if (!neighbourCell->isIntraFrequencyNeighbour)
|
||||
continue;
|
||||
seq_arr_push_back(pci_seq, &neighbourCell->physicalCellId, sizeof(int));
|
||||
const nr_a3_event_t *a3Event = get_a3_configuration(rrc, neighbourCell->physicalCellId);
|
||||
const nr_a3_event_t *a3Event = get_a3_configuration((gNB_RRC_INST *)rrc, neighbourCell->physicalCellId);
|
||||
if (!a3Event || is_default_a3_added)
|
||||
continue;
|
||||
if (a3Event->pci == -1)
|
||||
@@ -637,14 +626,15 @@ nr_rrc_reconfig_param_t get_RRCReconfiguration_params(gNB_RRC_INST *rrc, gNB_RRC
|
||||
rc_PER = prepare_periodic_event_report(rrc->measurementConfiguration.per_event);
|
||||
if (rrc->measurementConfiguration.a2_event)
|
||||
rc_A2 = prepare_a2_event_report(rrc->measurementConfiguration.a2_event);
|
||||
|
||||
measconfig = get_MeasConfig(mt, band, scs, rc_PER, rc_A2, rc_A3_seq, pci_seq);
|
||||
return get_MeasConfig(mt, band, scs, rc_PER, rc_A2, rc_A3_seq, neighbour_cells);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (UE->measConfig)
|
||||
free_MeasConfig(UE->measConfig);
|
||||
|
||||
UE->measConfig = measconfig;
|
||||
/** @brief Prepare the instance of RRCReconfigurationParams to be pass to RRC encoding */
|
||||
nr_rrc_reconfig_param_t get_RRCReconfiguration_params(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, uint8_t srb_reest_bitmap, bool drb_reestablish)
|
||||
{
|
||||
uint8_t xid = rrc_gNB_get_next_transaction_identifier(rrc->module_id);
|
||||
|
||||
// Re-establish PDCP for SRB2 only
|
||||
NR_SRB_ToAddModList_t *SRBs = createSRBlist(UE, srb_reest_bitmap);
|
||||
@@ -1070,6 +1060,7 @@ static void rrc_gNB_process_RRCReestablishmentComplete(gNB_RRC_INST *rrc, gNB_RR
|
||||
uint8_t new_xid = rrc_gNB_get_next_transaction_identifier(rrc->module_id);
|
||||
ue_p->xids[new_xid] = RRC_REESTABLISH_COMPLETE;
|
||||
nr_rrc_reconfig_param_t params = {.cell_group_config = cellGroupConfig,
|
||||
.meas_config = ue_p->measConfig,
|
||||
.drb_config_list = DRBs,
|
||||
.srb_config_list = SRBs,
|
||||
.transaction_id = new_xid};
|
||||
@@ -1189,6 +1180,7 @@ static void rrc_handle_RRCSetupRequest(gNB_RRC_INST *rrc,
|
||||
UE->nr_cellid = msg->nr_cellid;
|
||||
UE->masterCellGroup = cellGroupConfig;
|
||||
UE->ongoing_reconfiguration = false;
|
||||
UE->measConfig = nr_rrc_get_measconfig(rrc, UE->nr_cellid);
|
||||
activate_srb(UE, 1);
|
||||
rrc_gNB_generate_RRCSetup(0, msg->crnti, ue_context_p, msg->du2cu_rrc_container, msg->du2cu_rrc_container_length);
|
||||
}
|
||||
@@ -2137,6 +2129,17 @@ static void rrc_CU_process_ue_context_setup_response(MessageDef *msg_p, instance
|
||||
uper_decode_complete(NULL, &asn_DEF_NR_CellGroupConfig, (void **)&cellGroupConfig, (uint8_t *)cgc->buf, cgc->len);
|
||||
AssertFatal(dec_rval.code == RC_OK && dec_rval.consumed > 0, "Cell group config decode error\n");
|
||||
|
||||
if (resp->du_to_cu_rrc_info.meas_gap_config) {
|
||||
NR_MeasGapConfig_t *measGapConfig = NULL;
|
||||
asn_dec_rval_t dec_rval_mgc = uper_decode_complete(NULL,
|
||||
&asn_DEF_NR_MeasGapConfig,
|
||||
(void **)&measGapConfig,
|
||||
(uint8_t *)resp->du_to_cu_rrc_info.meas_gap_config->buf,
|
||||
resp->du_to_cu_rrc_info.meas_gap_config->len);
|
||||
AssertFatal(dec_rval_mgc.code == RC_OK && dec_rval_mgc.consumed > 0, "measGapConfig decode error\n");
|
||||
UE->measConfig->measGapConfig = measGapConfig;
|
||||
}
|
||||
|
||||
if (UE->masterCellGroup) {
|
||||
ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup);
|
||||
LOG_I(RRC, "UE %04x replacing existing CellGroupConfig with new one received from DU\n", UE->rnti);
|
||||
@@ -3087,6 +3090,28 @@ void rrc_gNB_generate_UeContextSetupRequest(const gNB_RRC_INST *rrc,
|
||||
*cu2du.ue_cap = copy_byte_array(ue_p->ue_cap_buffer);
|
||||
}
|
||||
|
||||
nr_rrc_du_container_t *du = get_du_for_ue((gNB_RRC_INST *)rrc, ue_p->rrc_ue_id);
|
||||
byte_array_t *meas_timing_config = NULL;
|
||||
if (du->mtc && ue_p->measConfig && ue_p->measConfig->measObjectToAddModList) {
|
||||
NR_MeasObjectToAddModList_t *mo_list = ue_p->measConfig->measObjectToAddModList;
|
||||
NR_ARFCN_ValueNR_t ssbFrequency0;
|
||||
for (int i = 0; i < mo_list->list.count; i++) {
|
||||
NR_MeasObjectToAddMod_t *mo = mo_list->list.array[i];
|
||||
if (mo->measObject.present == NR_MeasObjectToAddMod__measObject_PR_measObjectNR) {
|
||||
NR_MeasObjectNR_t *monr = mo->measObject.choice.measObjectNR;
|
||||
if (i == 0) {
|
||||
ssbFrequency0 = *monr->ssbFrequency;
|
||||
} else if (ssbFrequency0 != *monr->ssbFrequency) {
|
||||
meas_timing_config = calloc_or_fail(1, sizeof(*meas_timing_config));
|
||||
meas_timing_config->buf = calloc_or_fail(1, NR_RRC_BUF_SIZE);
|
||||
meas_timing_config->len = do_NR_MeasurementTimingConfiguration(du->mtc, meas_timing_config->buf, NR_RRC_BUF_SIZE);
|
||||
cu2du.meas_timing_config = meas_timing_config;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int nb_srb = 1;
|
||||
f1ap_srb_to_setup_t *srbs = calloc_or_fail(nb_srb, sizeof(*srbs));
|
||||
srbs[0].id = 2;
|
||||
|
||||
@@ -181,6 +181,19 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc,
|
||||
srbs[0].id = 1;
|
||||
srbs[1].id = 2;
|
||||
|
||||
free_MeasConfig(ue->measConfig);
|
||||
ue->measConfig = nr_rrc_get_measconfig(rrc, target_du->setup_req->cell[0].info.nr_cellid);
|
||||
byte_array_t *meas_config = calloc_or_fail(1, sizeof(*meas_config));
|
||||
meas_config->buf = calloc_or_fail(1, NR_RRC_BUF_SIZE);
|
||||
meas_config->len = do_NR_MeasConfig(ue->measConfig, meas_config->buf, NR_RRC_BUF_SIZE);
|
||||
|
||||
byte_array_t *meas_timing_config = NULL;
|
||||
if (target_du->mtc) {
|
||||
meas_timing_config = calloc_or_fail(1, sizeof(*meas_timing_config));
|
||||
meas_timing_config->buf = calloc_or_fail(1, NR_RRC_BUF_SIZE);
|
||||
meas_timing_config->len = do_NR_MeasurementTimingConfiguration(target_du->mtc, meas_timing_config->buf, NR_RRC_BUF_SIZE);
|
||||
}
|
||||
|
||||
byte_array_t *hpi = malloc_or_fail(sizeof(*hpi));
|
||||
*hpi = copy_byte_array(*ho_prep_info);
|
||||
|
||||
@@ -201,6 +214,8 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc,
|
||||
.drbs_len = nb_drb,
|
||||
.drbs = drbs,
|
||||
.cu_to_du_rrc_info.ho_prep_info = hpi,
|
||||
.cu_to_du_rrc_info.meas_config = meas_config,
|
||||
.cu_to_du_rrc_info.meas_timing_config = meas_timing_config,
|
||||
.gnb_du_ue_agg_mbr_ul = ue_agg_mbr,
|
||||
};
|
||||
rrc->mac_rrc.ue_context_setup_request(target_du->assoc_id, &ue_context_setup_req);
|
||||
|
||||
@@ -1871,8 +1871,7 @@ static void nr_rrc_ue_process_measConfig(rrcPerNB_t *rrc, NR_MeasConfig_t *const
|
||||
if (measConfig->measIdToAddModList)
|
||||
handle_measid_addmod(rrc, measConfig->measIdToAddModList, timers);
|
||||
|
||||
AssertFatal(!measConfig->measGapConfig, "Measurement gaps not yet supported\n");
|
||||
AssertFatal(!measConfig->measGapSharingConfig, "Measurement gaps not yet supported\n");
|
||||
LOG_W(NR_RRC, "Measurement gaps not yet supported!\n");
|
||||
|
||||
if (measConfig->s_MeasureConfig) {
|
||||
if (measConfig->s_MeasureConfig->present == NR_MeasConfig__s_MeasureConfig_PR_ssb_RSRP) {
|
||||
|
||||
Reference in New Issue
Block a user