Merge remote-tracking branch 'origin/rrc-sibs' into integration_2026_w16 (!3983)
Add SIB 3,4 support, configurable SIB2 and refactor CU/DU SIB management
This MR makes neighbour and inter-frequency configuration drive how
SIB2/SIB3/SIB4 are built and sent from CU to DUs. It standardizes SIB
payloads as byte_array_t with typed SIB IDs across RRC, F1AP and MAC,
reducing ad‑hoc buffer handling. Neighbour parsing, validation and
lookup are tightened.
Changes
- Minor refactor to gNB neighbour parsing and storage (shared PLMN
extraction, safer allocation, etc).
- Represent SIB containers uniformly as byte_array_t plus nr_sib_type_t,
and adapt F1AP, MAC and RRC users to the new container API.
- Make SIB2 cell-reselection information fully config-driven with
explicit bounds checking and SIB2 ASN.1 building from that config.
- Generate SIB3 intra-frequency neighbours from the per-cell neighbour
list and propagate them from CU to DU over F1, with MAC
decoding/attaching them to SystemInformation.
- Generate SIB4 inter-frequency neighbours from a new frequency_list
plus neighbour SIB3/SIB4 offsets, and propagate them from CU to DU
over F1, with MAC decoding/attaching them.
- Add basic ASN.1 round-trip tests for SIB2/SIB3/SIB4 and SIB4 range
checks, and update RRC docs to describe the neighbour/inter-frequency
configuration model.
Testing:
1. in gNB conf file:
cu_sibs = (2, 3, 4);
2. Update neighbour config file with SIB3/SIB4 conf:
(see documentation)
3. run gNB and UE as usual
---
Logs & configs: see MR on Gitlab
This commit is contained in:
@@ -17,6 +17,7 @@ gNBs =
|
|||||||
// Tracking area code, 0x0000 and 0xfffe are reserved values
|
// Tracking area code, 0x0000 and 0xfffe are reserved values
|
||||||
tracking_area_code = 1;
|
tracking_area_code = 1;
|
||||||
plmn_list = ({ mcc = 208; mnc = 99; mnc_length = 2; snssaiList = ({ sst = 1, sd = 0xffffff }) });
|
plmn_list = ({ mcc = 208; mnc = 99; mnc_length = 2; snssaiList = ({ sst = 1, sd = 0xffffff }) });
|
||||||
|
cu_sibs = ( 2, 3, 4 );
|
||||||
@include "neighbour-config.conf"
|
@include "neighbour-config.conf"
|
||||||
|
|
||||||
nr_cellid = 12345678L;
|
nr_cellid = 12345678L;
|
||||||
|
|||||||
@@ -5,6 +5,57 @@
|
|||||||
# for the 2-cell rfsim setup (gNB_ID 0xe00 & 0xb00) #
|
# for the 2-cell rfsim setup (gNB_ID 0xe00 & 0xb00) #
|
||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
# SIB2 #
|
||||||
|
############################################################
|
||||||
|
|
||||||
|
sib2_config : {
|
||||||
|
q_Hyst = 0;
|
||||||
|
cellReselectionPriority = 0;
|
||||||
|
threshServingLowP = 0;
|
||||||
|
threshServingLowQ = 0;
|
||||||
|
s_NonIntraSearchP = -1;
|
||||||
|
s_NonIntraSearchQ = -1;
|
||||||
|
q_RxLevMin = -56;
|
||||||
|
q_QualMin = -1;
|
||||||
|
s_IntraSearchP = 22;
|
||||||
|
s_IntraSearchQ = -1;
|
||||||
|
t_ReselectionNR = 1;
|
||||||
|
deriveSSB_IndexFromCell = 1;
|
||||||
|
|
||||||
|
speed_t_Evaluation = 0;
|
||||||
|
speed_t_HystNormal = 0;
|
||||||
|
speed_n_CellChangeMedium = 1;
|
||||||
|
speed_n_CellChangeHigh = 2;
|
||||||
|
speed_sf_Medium = 1;
|
||||||
|
speed_sf_High = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
# Per-frequency SIB4 inter-frequency configuration #
|
||||||
|
############################################################
|
||||||
|
|
||||||
|
frequency_list = (
|
||||||
|
{
|
||||||
|
absoluteFrequencySSB = 621312;
|
||||||
|
subcarrierSpacing = 1; # 30 kHz
|
||||||
|
band = 78;
|
||||||
|
|
||||||
|
frequency_config = (
|
||||||
|
{
|
||||||
|
cellReselectionPriority = 5;
|
||||||
|
threshX_HighP = 10;
|
||||||
|
threshX_LowP = 6;
|
||||||
|
threshX_HighQ = -1;
|
||||||
|
threshX_LowQ = -1;
|
||||||
|
q_OffsetFreq = 0;
|
||||||
|
q_RxLevMin = -56;
|
||||||
|
t_ReselectionNR = 1;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
neighbour_list = (
|
neighbour_list = (
|
||||||
##########################################################
|
##########################################################
|
||||||
# Entry USED BY gNB_ID = 0xe00 (nr_cellid = 1) #
|
# Entry USED BY gNB_ID = 0xe00 (nr_cellid = 1) #
|
||||||
@@ -21,6 +72,9 @@ neighbour_list = (
|
|||||||
band = 78;
|
band = 78;
|
||||||
plmn = { mcc = 208; mnc = 99; mnc_length = 2 };
|
plmn = { mcc = 208; mnc = 99; mnc_length = 2 };
|
||||||
tracking_area_code = 1;
|
tracking_area_code = 1;
|
||||||
|
q_OffsetCell = 2;
|
||||||
|
q_RxLevMinOffsetCell= 2;
|
||||||
|
q_QualMinOffsetCell = 1;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -40,6 +94,9 @@ neighbour_list = (
|
|||||||
band = 78;
|
band = 78;
|
||||||
plmn = { mcc = 208; mnc = 99; mnc_length = 2 };
|
plmn = { mcc = 208; mnc = 99; mnc_length = 2 };
|
||||||
tracking_area_code = 1;
|
tracking_area_code = 1;
|
||||||
|
q_OffsetCell = 2;
|
||||||
|
q_RxLevMinOffsetCell= 2;
|
||||||
|
q_QualMinOffsetCell = 1;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,8 +48,8 @@ typedef union checkedparam {
|
|||||||
int num_okintval; /* number of valid values in the checkingval array */
|
int num_okintval; /* number of valid values in the checkingval array */
|
||||||
} s1a;
|
} s1a;
|
||||||
struct {
|
struct {
|
||||||
int (*f2)(configmodule_interface_t *cfg,
|
int (*f2)(const configmodule_interface_t *cfg,
|
||||||
paramdef_t *param); /* check an integer against an authorized range, defined by its min and max value */
|
const paramdef_t *param); /* check an integer against an authorized range, defined by its min and max value */
|
||||||
int okintrange[CONFIG_MAX_NUMCHECKVAL]; /* integer array, store min and max values */
|
int okintrange[CONFIG_MAX_NUMCHECKVAL]; /* integer array, store min and max values */
|
||||||
|
|
||||||
} s2;
|
} s2;
|
||||||
|
|||||||
@@ -183,14 +183,35 @@ int config_check_intval(configmodule_interface_t *cfg, paramdef_t *param)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( int i=0; i<param->chkPptr->s1.num_okintval ; i++) {
|
if (param->type == TYPE_INT32 || param->type == TYPE_INT) {
|
||||||
if( *(param->uptr) == param->chkPptr->s1.okintval[i] ) {
|
if (param->iptr == NULL) {
|
||||||
return 0;
|
fprintf(stderr, "[CONFIG] config_check_intval: %s: NULL iptr\n", param->optname);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
const int v = *param->iptr;
|
||||||
|
for (int i = 0; i < param->chkPptr->s1.num_okintval; i++) {
|
||||||
|
if (v == param->chkPptr->s1.okintval[i])
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "[CONFIG] config_check_intval: %s: %i invalid value, authorized values:\n ", param->optname, v);
|
||||||
|
for (int i = 0; i < param->chkPptr->s1.num_okintval; i++) {
|
||||||
|
fprintf(stderr, " %i", param->chkPptr->s1.okintval[i]);
|
||||||
|
}
|
||||||
|
fprintf(stderr, " \n");
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
if (param->uptr == NULL) {
|
||||||
|
fprintf(stderr, "[CONFIG] config_check_intval: %s: NULL uptr\n", param->optname);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < param->chkPptr->s1.num_okintval; i++) {
|
||||||
|
if (*(param->uptr) == (uint32_t)param->chkPptr->s1.okintval[i]) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print_intvalueerror(param, "config_check_intval", param->chkPptr->s1.okintval, param->chkPptr->s1.num_okintval);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
print_intvalueerror(param,"config_check_intval", param->chkPptr->s1.okintval,param->chkPptr->s1.num_okintval);
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int config_check_modify_integer(configmodule_interface_t *cfg, paramdef_t *param)
|
int config_check_modify_integer(configmodule_interface_t *cfg, paramdef_t *param)
|
||||||
@@ -211,15 +232,38 @@ int config_check_modify_integer(configmodule_interface_t *cfg, paramdef_t *param
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int config_check_intrange(configmodule_interface_t *cfg, paramdef_t *param)
|
int config_check_intrange(const configmodule_interface_t *cfg, const paramdef_t *param)
|
||||||
{
|
{
|
||||||
UNUSED(cfg);
|
UNUSED(cfg);
|
||||||
if( *(param->iptr) >= param->chkPptr->s2.okintrange[0] && *(param->iptr) <= param->chkPptr->s2.okintrange[1] ) {
|
if( *(param->iptr) >= param->chkPptr->s2.okintrange[0] && *(param->iptr) <= param->chkPptr->s2.okintrange[1] ) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr,"[CONFIG] config_check_intrange: %s: %i invalid value, authorized range: %i %i\n",
|
fprintf(stderr,
|
||||||
param->optname, (int)*(param->uptr), param->chkPptr->s2.okintrange[0], param->chkPptr->s2.okintrange[1]);
|
"[CONFIG] config_check_intrange: %s: %i invalid value, authorized range: %i %i\n",
|
||||||
|
param->optname,
|
||||||
|
(int)*(param->iptr),
|
||||||
|
param->chkPptr->s2.okintrange[0],
|
||||||
|
param->chkPptr->s2.okintrange[1]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int config_check_uintrange(const configmodule_interface_t *cfg, const paramdef_t *param)
|
||||||
|
{
|
||||||
|
(void)cfg;
|
||||||
|
const uint32_t *v = param->uptr;
|
||||||
|
const int *range = param->chkPptr->s2.okintrange;
|
||||||
|
|
||||||
|
if (*v >= (uint32_t)range[0] && *v <= (uint32_t)range[1]) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr,
|
||||||
|
"[CONFIG] config_check_uintrange: %s: %u invalid, authorized range: %i %i\n",
|
||||||
|
param->optname,
|
||||||
|
*v,
|
||||||
|
range[0],
|
||||||
|
range[1]);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,8 @@ int config_get_processedint(configmodule_interface_t *cfg, paramdef_t *cfgoption
|
|||||||
/* functions to be used in parameters definition, to check parameters values */
|
/* functions to be used in parameters definition, to check parameters values */
|
||||||
int config_check_intval(configmodule_interface_t *cfg, paramdef_t *param);
|
int config_check_intval(configmodule_interface_t *cfg, paramdef_t *param);
|
||||||
int config_check_modify_integer(configmodule_interface_t *cfg, paramdef_t *param);
|
int config_check_modify_integer(configmodule_interface_t *cfg, paramdef_t *param);
|
||||||
int config_check_intrange(configmodule_interface_t *cfg, paramdef_t *param);
|
int config_check_intrange(const configmodule_interface_t *cfg, const paramdef_t *param);
|
||||||
|
int config_check_uintrange(const configmodule_interface_t *cfg, const paramdef_t *param);
|
||||||
int config_check_strval(configmodule_interface_t *cfg, paramdef_t *param);
|
int config_check_strval(configmodule_interface_t *cfg, paramdef_t *param);
|
||||||
int config_checkstr_assign_integer(configmodule_interface_t *cfg, paramdef_t *param);
|
int config_checkstr_assign_integer(configmodule_interface_t *cfg, paramdef_t *param);
|
||||||
const paramdef_t *config_get_paramdef_from_name(const paramdef_t *pd, int num, const char *name);
|
const paramdef_t *config_get_paramdef_from_name(const paramdef_t *pd, int num, const char *name);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "assertions.h"
|
#include "assertions.h"
|
||||||
#include "common/utils/utils.h"
|
#include "common/utils/utils.h"
|
||||||
|
#include "common/utils/ds/byte_array.h"
|
||||||
|
|
||||||
#define MAX_SI_GROUPS 3
|
#define MAX_SI_GROUPS 3
|
||||||
#define NR_MAX_PDSCH_TBS 3824
|
#define NR_MAX_PDSCH_TBS 3824
|
||||||
@@ -79,10 +80,32 @@ static inline const char *rnti_types(nr_rnti_type_t rr)
|
|||||||
// the total shift is 2 * 15, in dB scale thats 10log10(2^(15*2))
|
// the total shift is 2 * 15, in dB scale thats 10log10(2^(15*2))
|
||||||
#define SQ15_SQUARED_NORM_FACTOR_DB 90.3089986992
|
#define SQ15_SQUARED_NORM_FACTOR_DB 90.3089986992
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
NR_SIB_1 = 1,
|
||||||
|
NR_SIB_2,
|
||||||
|
NR_SIB_3,
|
||||||
|
NR_SIB_4,
|
||||||
|
NR_SIB_5,
|
||||||
|
NR_SIB_6,
|
||||||
|
NR_SIB_7,
|
||||||
|
NR_SIB_8,
|
||||||
|
NR_SIB_9,
|
||||||
|
NR_SIB_10,
|
||||||
|
NR_SIB_11,
|
||||||
|
NR_SIB_12,
|
||||||
|
NR_SIB_13,
|
||||||
|
NR_SIB_14,
|
||||||
|
NR_SIB_15,
|
||||||
|
NR_SIB_16,
|
||||||
|
NR_SIB_17,
|
||||||
|
NR_SIB_18,
|
||||||
|
NR_SIB_19,
|
||||||
|
NR_SIB_20,
|
||||||
|
NR_SIB_21,
|
||||||
|
} nr_sib_type_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t *SIB_buffer;
|
nr_sib_type_t SIB_type;
|
||||||
int SIB_size;
|
|
||||||
int SIB_type;
|
|
||||||
} nr_SIBs_t;
|
} nr_SIBs_t;
|
||||||
|
|
||||||
typedef struct nr_bandentry_s {
|
typedef struct nr_bandentry_s {
|
||||||
|
|||||||
+3
-3
@@ -154,7 +154,7 @@ These modes of operation are supported:
|
|||||||
- RedCap SIB1 v17 IEs supported
|
- RedCap SIB1 v17 IEs supported
|
||||||
- Coexistence of RedCap and Normal UEs
|
- Coexistence of RedCap and Normal UEs
|
||||||
- Handling of RedCap capability for small PDCP/RLC SN size
|
- Handling of RedCap capability for small PDCP/RLC SN size
|
||||||
- Scheduling of other SIBs (2, 19)
|
- Scheduling of other SIBs (2, 3, 4, 19)
|
||||||
- NTN
|
- NTN
|
||||||
- Support downlinkHARQ-FeedbackDisabled-r17
|
- Support downlinkHARQ-FeedbackDisabled-r17
|
||||||
- Support for 32 PDSCH and PUSCH HARQ processes per UE
|
- Support for 32 PDSCH and PUSCH HARQ processes per UE
|
||||||
@@ -194,7 +194,7 @@ These modes of operation are supported:
|
|||||||
|
|
||||||
- NR RRC (38.331) Rel 17 messages using new [asn1c](https://github.com/mouse07410/asn1c)
|
- NR RRC (38.331) Rel 17 messages using new [asn1c](https://github.com/mouse07410/asn1c)
|
||||||
- LTE RRC (36.331) also updated to Rel 15
|
- LTE RRC (36.331) also updated to Rel 15
|
||||||
- Generation of system information (SIB2)
|
- Generation of system information (SIB2, SIB3, SIB4)
|
||||||
- RRC can configure PDCP and SDAP (through E1), and RLC and MAC (through F1)
|
- RRC can configure PDCP and SDAP (through E1), and RLC and MAC (through F1)
|
||||||
- Interface with GTP-U (tunnel creation/handling for S1-U (NSA), N3 (SA), F1 interfaces)
|
- Interface with GTP-U (tunnel creation/handling for S1-U (NSA), N3 (SA), F1 interfaces)
|
||||||
- Integration of RRC messages and procedures supporting UE 5G SA connection
|
- Integration of RRC messages and procedures supporting UE 5G SA connection
|
||||||
@@ -380,7 +380,7 @@ These modes of operation are supported:
|
|||||||
- MIB processing
|
- MIB processing
|
||||||
- Scheduling of system information block 1 (SIB1) reception
|
- Scheduling of system information block 1 (SIB1) reception
|
||||||
* Other system information
|
* Other system information
|
||||||
- Scheduling of other system information blocks reception
|
- Scheduling of other system information blocks reception (SIB2, SIB3, SIB4)
|
||||||
* Random access procedure (needs improvement, there is still not a clear separation between MAC and PHY)
|
* Random access procedure (needs improvement, there is still not a clear separation between MAC and PHY)
|
||||||
- Mapping SSBs to multiple ROs
|
- Mapping SSBs to multiple ROs
|
||||||
- Scheduling of PRACH
|
- Scheduling of PRACH
|
||||||
|
|||||||
@@ -633,6 +633,98 @@ sequenceDiagram
|
|||||||
Note over TargetCU: Then: encode Handover Command, send NGAP HANDOVER REQUEST ACKNOWLEDGE
|
Note over TargetCU: Then: encode Handover Command, send NGAP HANDOVER REQUEST ACKNOWLEDGE
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Neighbour cells
|
||||||
|
|
||||||
|
#### SIB3/SIB4 and measurement-gap implementation
|
||||||
|
|
||||||
|
The following section documents the implementation-level control flow
|
||||||
|
for SIB3/SIB4 and measurement-gap handling in current OAI, using the configured
|
||||||
|
neighbor cell list as a shared input model.
|
||||||
|
|
||||||
|
Briefly, the three procedures are:
|
||||||
|
- SIB3: CU derives and provides intra-frequency neighbour SI, which DU broadcasts
|
||||||
|
and UE uses for autonomous idle/inactive intra-frequency reselection.
|
||||||
|
- SIB4: CU derives and provides inter-frequency carrier/neighbour SI, which DU
|
||||||
|
broadcasts and UE uses for autonomous idle/inactive inter-frequency reselection.
|
||||||
|
- MeasGap: CU/DU coordinate dedicated `MeasGapConfig` in UE `MeasConfig`, DU
|
||||||
|
scheduler interrupts transmission (`nr_measgap_scheduling()`) for the UE to
|
||||||
|
apply the MeasGap configuration in connected mode for gap-based measurements that
|
||||||
|
are sent as `MeasurementReport` and processed at CU-CP (`rrc_gNB_process_MeasurementReport()`).
|
||||||
|
Event-driven reports (for example A3) are used by CU-side mobility logic and can
|
||||||
|
trigger handover procedures.
|
||||||
|
|
||||||
|
### MeasGap
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant DM as Data model (neighbour_cell_configuration)
|
||||||
|
participant CU as CU-CP (RRC)
|
||||||
|
participant DU as DU (MAC/RRC)
|
||||||
|
participant UE as UE
|
||||||
|
|
||||||
|
CU-->>DM: Read serving + neighbour frequency/PCI/band data
|
||||||
|
DM-->>CU: Provide neighbour/frequency inputs for MeasConfig
|
||||||
|
CU->>CU: nr_rrc_get_measconfig
|
||||||
|
Note over CU: Build UE MeasConfig
|
||||||
|
CU->>CU: get_meas_timing_config (cell.mtc, ue.measConfig)
|
||||||
|
Note over CU: Check distinct ssbFrequency
|
||||||
|
alt one frequency only
|
||||||
|
Note over CU: returns NULL, no meas_timing_config sent to DU
|
||||||
|
else multiple frequencies
|
||||||
|
CU->>DU: F1AP UE Context Setup Request
|
||||||
|
Note over CU,DU: Includes cu_to_du_rrc_info.meas_timing_config
|
||||||
|
DU->>DU: create_measgap_config
|
||||||
|
DU->>DU: encode_measgap_config
|
||||||
|
DU-->>CU: F1AP UE Context Setup Response
|
||||||
|
Note over DU,CU: Includes du_to_cu_rrc_info.meas_gap_config
|
||||||
|
CU->>CU: get_meas_gap_config
|
||||||
|
Note over CU: Decode/store gap in UE MeasConfig (UE.measConfig.measGapConfig)
|
||||||
|
CU->>UE: RRCReconfiguration
|
||||||
|
Note over CU,UE: Includes MeasConfig.measGapConfig
|
||||||
|
rect rgba(210, 235, 255, 0.35)
|
||||||
|
Note over DU,UE: Measurement-gap window
|
||||||
|
DU->>DU: nr_measgap_scheduling
|
||||||
|
Note over DU: Interrupt transmission for meas gap
|
||||||
|
UE->>UE: nr_rrc_handle_meas_indication
|
||||||
|
Note over UE: Perform gap-based neighbour/inter-frequency measurements
|
||||||
|
UE-->>CU: MeasurementReport
|
||||||
|
end
|
||||||
|
Note over UE,CU: UL-DCCH MeasurementReport for configured events (e.g. A3)
|
||||||
|
CU->>CU: rrc_gNB_process_MeasurementReport
|
||||||
|
Note over CU: Run mobility decision logic (may trigger handover)
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### SIB3/SIB4
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant DM as Data model (neighbour_cell_configuration)
|
||||||
|
participant CU as CU-CP (RRC)
|
||||||
|
participant DU as DU
|
||||||
|
participant UE as UE
|
||||||
|
|
||||||
|
CU->>CU: cp_f1_served_cell_info_to_cell
|
||||||
|
CU->>DM: get_cell_neighbour_list
|
||||||
|
DM-->>CU: neighbour_cell_configuration for serving cell
|
||||||
|
CU->>CU: get_ssb_arfcn
|
||||||
|
Note over CU: Derive serving_ssb_arfcn from cell MTC
|
||||||
|
loop for each configured neighbour
|
||||||
|
Note over CU: Compare neighbour.absoluteFrequencySSB vs serving_ssb_arfcn
|
||||||
|
alt equal
|
||||||
|
CU->>CU: get_sib3_intra_freq_neighbors
|
||||||
|
else different
|
||||||
|
CU->>CU: get_sib4_inter_freq_neighbors
|
||||||
|
end
|
||||||
|
end
|
||||||
|
CU-->>DU: F1AP F1 Setup Response
|
||||||
|
DU-->>UE: BCCH-DL-SCH-Message
|
||||||
|
Note over DU,UE: Broadcast SystemInformation including configured SIB3/SIB4
|
||||||
|
UE->>UE: nr_rrc_ue_decode_NR_BCCH_DL_SCH_Message
|
||||||
|
Note over UE: Evaluate idle/inactive reselection criteria and timers
|
||||||
|
Note over UE,DU: After reselection, access/registration continues on selected cell DU as needed
|
||||||
|
```
|
||||||
|
|
||||||
## UE Context
|
## UE Context
|
||||||
|
|
||||||
UE context information is stored in `gNB_RRC_UE_t`, which includes:
|
UE context information is stored in `gNB_RRC_UE_t`, which includes:
|
||||||
|
|||||||
+158
-6
@@ -94,7 +94,81 @@ the [MAC configuration](../MAC/mac-usage.md) as well for SIB configuration.
|
|||||||
deactivated, a transparent SDAP header is prepended to packets, but no
|
deactivated, a transparent SDAP header is prepended to packets, but no
|
||||||
further processing is being done.
|
further processing is being done.
|
||||||
- `cu_sibs` (default: `[]`) list of SIBs to give to the DU for transmission.
|
- `cu_sibs` (default: `[]`) list of SIBs to give to the DU for transmission.
|
||||||
Currently, SIB2 is supported.
|
Currently supported:
|
||||||
|
- SIB2: serving-cell reselection parameters (configured in `sib2_config`)
|
||||||
|
- SIB3: intra-frequency neighbour cell list (neighbours on the same SSB ARFCN as the serving cell)
|
||||||
|
- SIB4: inter-frequency carriers + neighbour lists, grouped per `(absoluteFrequencySSB, subcarrierSpacing)`. Per-frequency fields (e.g. `cellReselectionPriority`, `threshX_HighP`, `threshX_LowP`, `q_OffsetFreq`) come from `frequency_list`
|
||||||
|
|
||||||
|
Example activation:
|
||||||
|
```
|
||||||
|
cu_sibs = ( 2, 3, 4 );
|
||||||
|
```
|
||||||
|
SIB2 is configured per-gNB in `sib2_config` (see below). SIB3/SIB4 are derived from the neighbour configuration:
|
||||||
|
- `neighbour_list` / `neighbour_cell_configuration`: neighbour identity + per-neighbour offsets (`q_OffsetCell`, etc.)
|
||||||
|
- `frequency_list`: per-frequency SIB4 reselection parameters (priority/thresholds/`q_OffsetFreq`)
|
||||||
|
|
||||||
|
Example `gNBs.[0].sib2_config`:
|
||||||
|
```
|
||||||
|
cu_sibs = ( 2 );
|
||||||
|
|
||||||
|
sib2_config : {
|
||||||
|
q_Hyst = 0;
|
||||||
|
cellReselectionPriority = 0;
|
||||||
|
threshServingLowP = 0;
|
||||||
|
threshServingLowQ = 4;
|
||||||
|
s_NonIntraSearchP = 10;
|
||||||
|
s_NonIntraSearchQ = 8;
|
||||||
|
q_RxLevMin = -56;
|
||||||
|
q_QualMin = -18;
|
||||||
|
s_IntraSearchP = 22;
|
||||||
|
s_IntraSearchQ = 20;
|
||||||
|
t_ReselectionNR = 1;
|
||||||
|
deriveSSB_IndexFromCell = 1;
|
||||||
|
|
||||||
|
speed_t_Evaluation = 0;
|
||||||
|
speed_t_HystNormal = 0;
|
||||||
|
speed_n_CellChangeMedium = 1;
|
||||||
|
speed_n_CellChangeHigh = 2;
|
||||||
|
speed_sf_Medium = 1;
|
||||||
|
speed_sf_High = 0;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
#### SIB3/SIB4 and measurement gaps
|
||||||
|
|
||||||
|
This section summarizes how SIB3/SIB4 and measurement gaps relate in NR and how OAI currently implements them.
|
||||||
|
|
||||||
|
From 3GPP TS 38.331:
|
||||||
|
|
||||||
|
- `SIB3` carries intra-frequency reselection information (`intraFreqNeighCellList`).
|
||||||
|
- `SIB4` carries inter-frequency reselection information (`interFreqCarrierFreqList`
|
||||||
|
and per-carrier neighbour lists).
|
||||||
|
- `MeasGapConfig` is part of dedicated `MeasConfig` (typically sent in
|
||||||
|
`RRCReconfiguration`) and controls measurement gaps in connected mode.
|
||||||
|
|
||||||
|
In other words:
|
||||||
|
|
||||||
|
- SIB3/SIB4 are broadcast SI for idle/inactive (`RRC_IDLE` / `RRC_INACTIVE`)
|
||||||
|
reselection behavior: UE performs autonomous cell reselection using broadcast
|
||||||
|
SI (SIB3 for intra-frequency, SIB4 for inter-frequency).
|
||||||
|
- `MeasGapConfig` is a dedicated UE measurement behavior and applies to
|
||||||
|
connected mode (`RRC_CONNECTED`): the network configures what the UE
|
||||||
|
measures and reports (periodic and event-based, e.g., A3), and those reports
|
||||||
|
are used by CU-CP mobility logic (including handover decisions).
|
||||||
|
|
||||||
|
MeasGap does not depend on SIB3/SIB4, however they share the same underlying
|
||||||
|
neighbour/frequency data model, which is the common source of serving +
|
||||||
|
neighbour frequency information (see also [Neighbor-gNB configuration](#neighbour-gnb-configuration)):
|
||||||
|
|
||||||
|
- SIB3/SIB4 generation is done on the CU-CP side (inside `rrc_gNB_du.c`) from
|
||||||
|
neighbour/frequency configuration and serving-cell MTC-derived ARFCN.
|
||||||
|
- Measurement-gap configuration also starts from the same neighbour/frequency
|
||||||
|
model: CU uses neighbour fields (frequency/PCI/band) to build UE
|
||||||
|
`MeasConfig` measurement objects, then DU derives/encodes gap parameters
|
||||||
|
from CU-provided timing (`meas_timing_config`) and returns `meas_gap_config`
|
||||||
|
for CU forwarding in dedicated `RRCReconfiguration`.
|
||||||
|
|
||||||
|
Detailed implementation flow and sequence diagrams are documented in [`rrc-dev.md`](./rrc-dev.md).
|
||||||
|
|
||||||
#### UE-specific configuration
|
#### UE-specific configuration
|
||||||
|
|
||||||
@@ -105,12 +179,64 @@ the [MAC configuration](../MAC/mac-usage.md) as well for SIB configuration.
|
|||||||
|
|
||||||
Refer to the [handover tutorial](../handover-tutorial.md) for detailed information about gNB neighbors and handover procedures.
|
Refer to the [handover tutorial](../handover-tutorial.md) for detailed information about gNB neighbors and handover procedures.
|
||||||
|
|
||||||
|
##### Configuration structure and key semantics
|
||||||
|
|
||||||
|
The neighbour configuration is a 2-level structure:
|
||||||
|
|
||||||
|
- Outer list: `neighbour_list`
|
||||||
|
- Key: `nr_cellid` of the serving cell
|
||||||
|
- One entry per serving cell
|
||||||
|
- Inner list: `neighbour_cell_configuration`
|
||||||
|
- Actual neighbour cells for that serving cell
|
||||||
|
- Contains neighbour fields such as `gNB_ID`, neighbour `nr_cellid`, `physical_cellId`, frequency, PLMN, etc.
|
||||||
|
|
||||||
|
This same core configuration model is reused by multiple RRC procedures.
|
||||||
|
|
||||||
|
- SIB3/SIB4 generation: uses neighbour identity/frequency/offset fields to derive
|
||||||
|
intra/inter-frequency SI.
|
||||||
|
- Connected-mode measurement config: uses neighbour frequency/PCI/band fields to build
|
||||||
|
UE `MeasConfig` measurement objects.
|
||||||
|
- Handover-related procedures: reuse neighbour identity fields (e.g., cell ID/PCI/PLMN
|
||||||
|
TAC, gNB ID) for target selection and for populating target-cell information carried
|
||||||
|
in NGAP handover messages.
|
||||||
|
|
||||||
|
Conceptually, for each serving cell the RRC keeps:
|
||||||
|
|
||||||
|
- A per-frequency table (`inter_freqs`): one entry per `(absoluteFrequencySSB,
|
||||||
|
subcarrierSpacing)` used for SIB4, containing the SIB4 per-frequency fields
|
||||||
|
(priority, thresholds, `q_OffsetFreq`, `q_RxLevMin`, `t_ReselectionNR`).
|
||||||
|
- A per-neighbour list (`neighbour_cells`): one entry per neighbour, with
|
||||||
|
identity (cell ID, PCI, PLMN, TAC), frequency (`absoluteFrequencySSB`,
|
||||||
|
`subcarrierSpacing`, `band`), and SIB3/SIB4 per-neighbour offsets
|
||||||
|
(`q_OffsetCell`, `q_RxLevMinOffsetCell`, `q_QualMinOffsetCell`).
|
||||||
|
- A link from neighbours to frequencies: each neighbour implicitly points to
|
||||||
|
the matching `inter_freqs` entry via its `(absoluteFrequencySSB,
|
||||||
|
subcarrierSpacing)`; if no such frequency exists, it is treated as having no
|
||||||
|
SIB4 per-frequency configuration.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
- In `neighbour_list`, only `nr_cellid` is used as the key for lookup.
|
||||||
|
- `physical_cellId` belongs to neighbour-cell entries in `neighbour_cell_configuration` (inner list).
|
||||||
|
- `nr_cellid` entries in `neighbour_list` should be unique to avoid ambiguous lookup.
|
||||||
|
- Intra-frequency neighbours (SIB3) are derived only from the per-cell
|
||||||
|
`neighbour_cell_configuration` on the serving carrier.
|
||||||
|
|
||||||
|
At configuration time (`gnb_config.c`), neighbours are parsed into `neighbour_cells`,
|
||||||
|
per-neighbour SIB3/SIB4 offsets are validated, and a per-frequency array `inter_freqs`
|
||||||
|
is built by grouping neighbours by `(absoluteFrequencySSB, subcarrierSpacing)` and
|
||||||
|
in-range SIB4 per-frequency fields (`cellReselectionPriority`, `threshX_HighP/L`,
|
||||||
|
`q_OffsetFreq`) across neighbours on the same ARFCN. At SIB4 build time, the RRC uses
|
||||||
|
`inter_freqs` to create one `InterFreqCarrierFreqInfo` per ARFCN for inter-frequency
|
||||||
|
carriers (ARFCN different from the serving SSB ARFCN) and attaches all neighbours whose
|
||||||
|
`inter_freq_idx` points to that frequency entry.
|
||||||
|
|
||||||
##### Required configuration parameters
|
##### Required configuration parameters
|
||||||
|
|
||||||
To define a neighbor cell in the configuration file, the following parameters are required:
|
To define a neighbour cell in the configuration file, the following parameters are required:
|
||||||
|
|
||||||
- `gNB_ID` - identifier of the neighbor gNB (e.g., `0xe01`)
|
- `gNB_ID` - identifier of the neighbour gNB (e.g., `0xe01`)
|
||||||
- `nr_cellid` - cell identifier of the neighbor cell (e.g., `11111111`)
|
- `nr_cellid` - cell identifier of the neighbour cell (e.g., `11111111`)
|
||||||
- `physical_cellId` - physical cell ID for radio identification (e.g., `1`)
|
- `physical_cellId` - physical cell ID for radio identification (e.g., `1`)
|
||||||
- `absoluteFrequencySSB` - SSB frequency in ARFCN notation (e.g., `643296`)
|
- `absoluteFrequencySSB` - SSB frequency in ARFCN notation (e.g., `643296`)
|
||||||
- `subcarrierSpacing` - numerology index: 0=15kHz, 1=30kHz, 2=60kHz, 3=120kHz
|
- `subcarrierSpacing` - numerology index: 0=15kHz, 1=30kHz, 2=60kHz, 3=120kHz
|
||||||
@@ -123,19 +249,45 @@ To define a neighbor cell in the configuration file, the following parameters ar
|
|||||||
|
|
||||||
Example configuration structure:
|
Example configuration structure:
|
||||||
```
|
```
|
||||||
|
# Per-frequency SIB4 configuration (one entry per ARFCN), shared by all cells
|
||||||
|
frequency_list = (
|
||||||
|
{
|
||||||
|
absoluteFrequencySSB = 643296;
|
||||||
|
subcarrierSpacing = 1; # 30 kHz
|
||||||
|
band = 78;
|
||||||
|
|
||||||
|
frequency_config = (
|
||||||
|
{
|
||||||
|
cellReselectionPriority = 5;
|
||||||
|
threshX_HighP = 10;
|
||||||
|
threshX_LowP = 6;
|
||||||
|
q_OffsetFreq = 0;
|
||||||
|
# Optional: threshX_HighQ, threshX_LowQ, etc.
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
# Per-cell neighbour configuration; neighbours reference frequency_list via ARFCN/SCS
|
||||||
neighbour_list = (
|
neighbour_list = (
|
||||||
{
|
{
|
||||||
nr_cellid = 12345678;
|
nr_cellid = 12345678;
|
||||||
|
|
||||||
neighbour_cell_configuration = (
|
neighbour_cell_configuration = (
|
||||||
{
|
{
|
||||||
gNB_ID = 0xe01;
|
gNB_ID = 0xe01;
|
||||||
nr_cellid = 11111111;
|
nr_cellid = 11111111;
|
||||||
physical_cellId = 1;
|
physical_cellId = 1;
|
||||||
absoluteFrequencySSB = 643296;
|
absoluteFrequencySSB = 643296; # ARFCN used to look up matching entry in frequency_list
|
||||||
subcarrierSpacing = 1; # 30 kHz
|
subcarrierSpacing = 1; # 30 kHz
|
||||||
band = 78;
|
band = 78;
|
||||||
plmn = { mcc = 001; mnc = 01; mnc_length = 2 };
|
plmn = { mcc = 001; mnc = 01; mnc_length = 2 };
|
||||||
tracking_area_code = 1;
|
tracking_area_code = 1;
|
||||||
|
|
||||||
|
# Example per-neighbour offsets (SIB3/SIB4)
|
||||||
|
q_OffsetCell = 0;
|
||||||
|
q_RxLevMinOffsetCell = -1;
|
||||||
|
q_QualMinOffsetCell = -1;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -512,8 +512,16 @@ configuration, e.g. [neighbour-config-rfsim.conf](../../ci-scripts/conf_files/ne
|
|||||||
This configuration can also be present in a different file and included in the
|
This configuration can also be present in a different file and included in the
|
||||||
gNB configuration file with `@include "neighbour-config-rfsim.conf"`.
|
gNB configuration file with `@include "neighbour-config-rfsim.conf"`.
|
||||||
|
|
||||||
For each gNB there is a `neighbour_cell_configuration` linked to its serving
|
The neighbor configuration is nested:
|
||||||
cell ID.
|
|
||||||
|
- `neighbour_list` outer entries are keyed by serving `nr_cellid`
|
||||||
|
- each outer entry contains `neighbour_cell_configuration`, i.e., the list of neighbor cells for that serving cell
|
||||||
|
|
||||||
|
In this model:
|
||||||
|
|
||||||
|
- outer `nr_cellid` entries should be unique
|
||||||
|
- neighbor `physical_cellId` values are defined in inner neighbor entries
|
||||||
|
- the same serving-cell keyed neighbor mapping is used for both F1 and N2 handover logic
|
||||||
|
|
||||||
See the example above for `neighbour-config-ho.conf`. The same configuration
|
See the example above for `neighbour-config-ho.conf`. The same configuration
|
||||||
is for both F1 and N2 handover.
|
is for both F1 and N2 handover.
|
||||||
|
|||||||
@@ -161,8 +161,7 @@ typedef struct f1ap_du_register_req_s {
|
|||||||
|
|
||||||
typedef struct f1ap_sib_msg_s {
|
typedef struct f1ap_sib_msg_s {
|
||||||
/// RRC container with system information owned by gNB-CU
|
/// RRC container with system information owned by gNB-CU
|
||||||
uint8_t *SI_container;
|
byte_array_t SI_container;
|
||||||
int SI_container_length;
|
|
||||||
/// SIB block type, e.g. 2 for sibType2
|
/// SIB block type, e.g. 2 for sibType2
|
||||||
int SI_type;
|
int SI_type;
|
||||||
} f1ap_sib_msg_t;
|
} f1ap_sib_msg_t;
|
||||||
|
|||||||
@@ -627,10 +627,10 @@ static void encode_cells_to_activate(const served_cells_to_activate_t *cell, F1A
|
|||||||
F1AP_GNB_CUSystemInformation_t *gNB_CUSystemInformation =
|
F1AP_GNB_CUSystemInformation_t *gNB_CUSystemInformation =
|
||||||
&cells_to_be_activated_itemExtIEs->extensionValue.choice.GNB_CUSystemInformation;
|
&cells_to_be_activated_itemExtIEs->extensionValue.choice.GNB_CUSystemInformation;
|
||||||
const f1ap_sib_msg_t *SI_msg = &cell->SI_msg[n];
|
const f1ap_sib_msg_t *SI_msg = &cell->SI_msg[n];
|
||||||
if (SI_msg->SI_container != NULL) {
|
if (SI_msg->SI_container.buf != NULL) {
|
||||||
asn1cSequenceAdd(gNB_CUSystemInformation->sibtypetobeupdatedlist.list, F1AP_SibtypetobeupdatedListItem_t, sib_item);
|
asn1cSequenceAdd(gNB_CUSystemInformation->sibtypetobeupdatedlist.list, F1AP_SibtypetobeupdatedListItem_t, sib_item);
|
||||||
sib_item->sIBtype = SI_msg->SI_type;
|
sib_item->sIBtype = SI_msg->SI_type;
|
||||||
OCTET_STRING_fromBuf(&sib_item->sIBmessage, (const char *)SI_msg->SI_container, SI_msg->SI_container_length);
|
OCTET_STRING_fromBuf(&sib_item->sIBmessage, (const char *)SI_msg->SI_container.buf, SI_msg->SI_container.len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -670,7 +670,7 @@ static bool decode_cells_to_activate(served_cells_to_activate_t *out, const F1AP
|
|||||||
F1AP_SibtypetobeupdatedListItem_t *sib_item = gNB_CUSystemInformation->sibtypetobeupdatedlist.list.array[s];
|
F1AP_SibtypetobeupdatedListItem_t *sib_item = gNB_CUSystemInformation->sibtypetobeupdatedlist.list.array[s];
|
||||||
/* SI container */
|
/* SI container */
|
||||||
f1ap_sib_msg_t *SI_msg = &out->SI_msg[s];
|
f1ap_sib_msg_t *SI_msg = &out->SI_msg[s];
|
||||||
SI_msg->SI_container = cp_octet_string(&sib_item->sIBmessage, &SI_msg->SI_container_length);
|
SI_msg->SI_container.buf = cp_octet_string(&sib_item->sIBmessage, (int *)&SI_msg->SI_container.len);
|
||||||
/* SIB type */
|
/* SIB type */
|
||||||
SI_msg->SI_type = sib_item->sIBtype;
|
SI_msg->SI_type = sib_item->sIBtype;
|
||||||
}
|
}
|
||||||
@@ -1163,8 +1163,8 @@ bool eq_f1ap_setup_response(const f1ap_setup_resp_t *a, const f1ap_setup_resp_t
|
|||||||
for (int j = 0; j < b->cells_to_activate[i].num_SI; j++) {
|
for (int j = 0; j < b->cells_to_activate[i].num_SI; j++) {
|
||||||
const f1ap_sib_msg_t *a_SI_msg = &a->cells_to_activate[i].SI_msg[j];
|
const f1ap_sib_msg_t *a_SI_msg = &a->cells_to_activate[i].SI_msg[j];
|
||||||
const f1ap_sib_msg_t *b_SI_msg = &b->cells_to_activate[i].SI_msg[j];
|
const f1ap_sib_msg_t *b_SI_msg = &b->cells_to_activate[i].SI_msg[j];
|
||||||
_EQ_CHECK_INT(*a_SI_msg->SI_container, *b_SI_msg->SI_container);
|
if (!eq_byte_array(&a_SI_msg->SI_container, &b_SI_msg->SI_container))
|
||||||
_EQ_CHECK_INT(a_SI_msg->SI_container_length, b_SI_msg->SI_container_length);
|
return false;
|
||||||
_EQ_CHECK_INT(a_SI_msg->SI_type, b_SI_msg->SI_type);
|
_EQ_CHECK_INT(a_SI_msg->SI_type, b_SI_msg->SI_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1201,9 +1201,7 @@ f1ap_setup_resp_t cp_f1ap_setup_response(const f1ap_setup_resp_t *msg)
|
|||||||
for (int j = 0; j < cp_cell->num_SI; j++) {
|
for (int j = 0; j < cp_cell->num_SI; j++) {
|
||||||
f1ap_sib_msg_t *cp_SI_msg = &cp_cell->SI_msg[j];
|
f1ap_sib_msg_t *cp_SI_msg = &cp_cell->SI_msg[j];
|
||||||
const f1ap_sib_msg_t *b_SI_msg = &msg_cell->SI_msg[j];
|
const f1ap_sib_msg_t *b_SI_msg = &msg_cell->SI_msg[j];
|
||||||
cp_SI_msg->SI_container_length = b_SI_msg->SI_container_length;
|
cp_SI_msg->SI_container = copy_byte_array(b_SI_msg->SI_container);
|
||||||
cp_SI_msg->SI_container = malloc_or_fail(cp_SI_msg->SI_container_length);
|
|
||||||
memcpy(cp_SI_msg->SI_container, b_SI_msg->SI_container, b_SI_msg->SI_container_length);
|
|
||||||
cp_SI_msg->SI_type = b_SI_msg->SI_type;
|
cp_SI_msg->SI_type = b_SI_msg->SI_type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1221,8 +1219,7 @@ void free_f1ap_setup_response(const f1ap_setup_resp_t *msg)
|
|||||||
free(msg->gNB_CU_name);
|
free(msg->gNB_CU_name);
|
||||||
for (int i = 0; i < msg->num_cells_to_activate; i++)
|
for (int i = 0; i < msg->num_cells_to_activate; i++)
|
||||||
for (int j = 0; j < msg->cells_to_activate[i].num_SI; j++)
|
for (int j = 0; j < msg->cells_to_activate[i].num_SI; j++)
|
||||||
if (msg->cells_to_activate[i].SI_msg[j].SI_container_length > 0)
|
free_byte_array(msg->cells_to_activate[i].SI_msg[j].SI_container);
|
||||||
free(msg->cells_to_activate[i].SI_msg[j].SI_container);
|
|
||||||
free(msg->cells_to_activate);
|
free(msg->cells_to_activate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1798,7 +1795,7 @@ void free_f1ap_cu_configuration_update(const f1ap_gnb_cu_configuration_update_t
|
|||||||
{
|
{
|
||||||
for (int i = 0; i < msg->num_cells_to_activate; i++)
|
for (int i = 0; i < msg->num_cells_to_activate; i++)
|
||||||
for (int j = 0; j < msg->cells_to_activate[i].num_SI; j++)
|
for (int j = 0; j < msg->cells_to_activate[i].num_SI; j++)
|
||||||
free(msg->cells_to_activate[i].SI_msg[j].SI_container);
|
free_byte_array(msg->cells_to_activate[i].SI_msg[j].SI_container);
|
||||||
free(msg->cells_to_activate);
|
free(msg->cells_to_activate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1819,8 +1816,8 @@ bool eq_f1ap_cu_configuration_update(const f1ap_gnb_cu_configuration_update_t *a
|
|||||||
for (int s = 0; s < a->cells_to_activate[i].num_SI; s++) {
|
for (int s = 0; s < a->cells_to_activate[i].num_SI; s++) {
|
||||||
f1ap_sib_msg_t *a_sib_msg = &a->cells_to_activate[i].SI_msg[s];
|
f1ap_sib_msg_t *a_sib_msg = &a->cells_to_activate[i].SI_msg[s];
|
||||||
f1ap_sib_msg_t *b_sib_msg = &b->cells_to_activate[i].SI_msg[s];
|
f1ap_sib_msg_t *b_sib_msg = &b->cells_to_activate[i].SI_msg[s];
|
||||||
_EQ_CHECK_INT(*a_sib_msg->SI_container, *b_sib_msg->SI_container);
|
if (!eq_byte_array(&a_sib_msg->SI_container, &b_sib_msg->SI_container))
|
||||||
_EQ_CHECK_INT(a_sib_msg->SI_container_length, b_sib_msg->SI_container_length);
|
return false;
|
||||||
_EQ_CHECK_INT(a_sib_msg->SI_type, b_sib_msg->SI_type);
|
_EQ_CHECK_INT(a_sib_msg->SI_type, b_sib_msg->SI_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1842,9 +1839,7 @@ f1ap_gnb_cu_configuration_update_t cp_f1ap_cu_configuration_update(const f1ap_gn
|
|||||||
for (int s = 0; s < cp.cells_to_activate[i].num_SI; s++) {
|
for (int s = 0; s < cp.cells_to_activate[i].num_SI; s++) {
|
||||||
cp.cells_to_activate[i].SI_msg[s] = msg->cells_to_activate[i].SI_msg[s];
|
cp.cells_to_activate[i].SI_msg[s] = msg->cells_to_activate[i].SI_msg[s];
|
||||||
f1ap_sib_msg_t *SI_msg = &cp.cells_to_activate[i].SI_msg[s];
|
f1ap_sib_msg_t *SI_msg = &cp.cells_to_activate[i].SI_msg[s];
|
||||||
SI_msg->SI_container = calloc_or_fail(SI_msg->SI_container_length, sizeof(*SI_msg->SI_container));
|
SI_msg->SI_container = copy_byte_array(msg->cells_to_activate[i].SI_msg[s].SI_container);
|
||||||
for (int j = 0; j < SI_msg->SI_container_length; j++)
|
|
||||||
SI_msg->SI_container[j] = msg->cells_to_activate[i].SI_msg[s].SI_container[j];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cp;
|
return cp;
|
||||||
@@ -2153,8 +2148,8 @@ bool eq_f1ap_du_configuration_update_acknowledge(const f1ap_gnb_du_configuration
|
|||||||
return false;
|
return false;
|
||||||
_EQ_CHECK_INT(a->cells_to_activate[i].num_SI, b->cells_to_activate[i].num_SI);
|
_EQ_CHECK_INT(a->cells_to_activate[i].num_SI, b->cells_to_activate[i].num_SI);
|
||||||
for (int s = 0; s < a->cells_to_activate[i].num_SI; s++) {
|
for (int s = 0; s < a->cells_to_activate[i].num_SI; s++) {
|
||||||
_EQ_CHECK_INT(*a->cells_to_activate[i].SI_msg[s].SI_container, *b->cells_to_activate[i].SI_msg[s].SI_container);
|
if (!eq_byte_array(&a->cells_to_activate[i].SI_msg[s].SI_container, &b->cells_to_activate[i].SI_msg[s].SI_container))
|
||||||
_EQ_CHECK_INT(a->cells_to_activate[i].SI_msg[s].SI_container_length, b->cells_to_activate[i].SI_msg[s].SI_container_length);
|
return false;
|
||||||
_EQ_CHECK_INT(a->cells_to_activate[i].SI_msg[s].SI_type, b->cells_to_activate[i].SI_msg[s].SI_type);
|
_EQ_CHECK_INT(a->cells_to_activate[i].SI_msg[s].SI_type, b->cells_to_activate[i].SI_msg[s].SI_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2180,9 +2175,7 @@ f1ap_gnb_du_configuration_update_acknowledge_t cp_f1ap_du_configuration_update_a
|
|||||||
f1ap_sib_msg_t *cp_sib = &cp.cells_to_activate[i].SI_msg[s];
|
f1ap_sib_msg_t *cp_sib = &cp.cells_to_activate[i].SI_msg[s];
|
||||||
const f1ap_sib_msg_t *msg_sib = &msg->cells_to_activate[i].SI_msg[s];
|
const f1ap_sib_msg_t *msg_sib = &msg->cells_to_activate[i].SI_msg[s];
|
||||||
cp_sib->SI_type = msg_sib->SI_type;
|
cp_sib->SI_type = msg_sib->SI_type;
|
||||||
cp_sib->SI_container_length = msg_sib->SI_container_length;
|
cp_sib->SI_container = copy_byte_array(msg_sib->SI_container);
|
||||||
cp_sib->SI_container = calloc_or_fail(cp_sib->SI_container_length, sizeof(*cp_sib->SI_container));
|
|
||||||
memcpy(cp_sib->SI_container, msg_sib->SI_container, cp_sib->SI_container_length);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cp;
|
return cp;
|
||||||
@@ -2196,9 +2189,7 @@ void free_f1ap_du_configuration_update_acknowledge(const f1ap_gnb_du_configurati
|
|||||||
// SI_container
|
// SI_container
|
||||||
for (int i = 0; i < msg->num_cells_to_activate; i++) {
|
for (int i = 0; i < msg->num_cells_to_activate; i++) {
|
||||||
for (int j = 0; j < msg->cells_to_activate[i].num_SI; j++) {
|
for (int j = 0; j < msg->cells_to_activate[i].num_SI; j++) {
|
||||||
if (msg->cells_to_activate[i].SI_msg[j].SI_container) {
|
free_byte_array(msg->cells_to_activate[i].SI_msg[j].SI_container);
|
||||||
free(msg->cells_to_activate[i].SI_msg[j].SI_container);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(msg->cells_to_activate);
|
free(msg->cells_to_activate);
|
||||||
|
|||||||
@@ -313,9 +313,9 @@ static void test_f1ap_setup_response(void)
|
|||||||
int SI_container_length = strlen(s) + 1;
|
int SI_container_length = strlen(s) + 1;
|
||||||
orig.cells_to_activate[0].num_SI = 1;
|
orig.cells_to_activate[0].num_SI = 1;
|
||||||
f1ap_sib_msg_t *SI_msg = &orig.cells_to_activate[0].SI_msg[0];
|
f1ap_sib_msg_t *SI_msg = &orig.cells_to_activate[0].SI_msg[0];
|
||||||
SI_msg->SI_container = malloc_or_fail(SI_container_length);
|
SI_msg->SI_container.buf = malloc_or_fail(SI_container_length);
|
||||||
memcpy(SI_msg->SI_container, (uint8_t *)s, SI_container_length);
|
memcpy(SI_msg->SI_container.buf, s, SI_container_length);
|
||||||
SI_msg->SI_container_length = SI_container_length;
|
SI_msg->SI_container.len = SI_container_length;
|
||||||
SI_msg->SI_type = 7;
|
SI_msg->SI_type = 7;
|
||||||
}
|
}
|
||||||
F1AP_F1AP_PDU_t *f1enc = encode_f1ap_setup_response(&orig);
|
F1AP_F1AP_PDU_t *f1enc = encode_f1ap_setup_response(&orig);
|
||||||
@@ -609,10 +609,10 @@ static void test_f1ap_du_configuration_update_acknowledge(void)
|
|||||||
orig.cells_to_activate[0].plmn.mnc_digit_length = 2;
|
orig.cells_to_activate[0].plmn.mnc_digit_length = 2;
|
||||||
orig.cells_to_activate[0].num_SI = 1;
|
orig.cells_to_activate[0].num_SI = 1;
|
||||||
orig.cells_to_activate[0].SI_msg[0].SI_type = 7;
|
orig.cells_to_activate[0].SI_msg[0].SI_type = 7;
|
||||||
orig.cells_to_activate[0].SI_msg[0].SI_container_length = 10;
|
orig.cells_to_activate[0].SI_msg[0].SI_container.len = 10;
|
||||||
orig.cells_to_activate[0].SI_msg[0].SI_container = malloc(sizeof(uint8_t) * 10);
|
orig.cells_to_activate[0].SI_msg[0].SI_container.buf = malloc(sizeof(uint8_t) * 10);
|
||||||
for (int i = 0; i < orig.cells_to_activate[0].SI_msg[0].SI_container_length; i++) {
|
for (int i = 0; i < (int)orig.cells_to_activate[0].SI_msg[0].SI_container.len; i++) {
|
||||||
orig.cells_to_activate[0].SI_msg[0].SI_container[i] = i;
|
orig.cells_to_activate[0].SI_msg[0].SI_container.buf[i] = i;
|
||||||
}
|
}
|
||||||
// ASN.1 enc/dec
|
// ASN.1 enc/dec
|
||||||
F1AP_F1AP_PDU_t *f1enc = encode_f1ap_du_configuration_update_acknowledge(&orig);
|
F1AP_F1AP_PDU_t *f1enc = encode_f1ap_du_configuration_update_acknowledge(&orig);
|
||||||
@@ -655,8 +655,8 @@ static void test_f1ap_cu_configuration_update(void)
|
|||||||
orig.cells_to_activate[0].plmn.mnc_digit_length = 2;
|
orig.cells_to_activate[0].plmn.mnc_digit_length = 2;
|
||||||
orig.cells_to_activate[0].num_SI = 1;
|
orig.cells_to_activate[0].num_SI = 1;
|
||||||
orig.cells_to_activate[0].SI_msg[0].SI_type = 7;
|
orig.cells_to_activate[0].SI_msg[0].SI_type = 7;
|
||||||
orig.cells_to_activate[0].SI_msg[0].SI_container_length = 10;
|
orig.cells_to_activate[0].SI_msg[0].SI_container.len = 10;
|
||||||
orig.cells_to_activate[0].SI_msg[0].SI_container = malloc(sizeof(uint8_t) * 10);
|
orig.cells_to_activate[0].SI_msg[0].SI_container.buf = malloc(sizeof(uint8_t) * 10);
|
||||||
F1AP_F1AP_PDU_t *f1enc = encode_f1ap_cu_configuration_update(&orig);
|
F1AP_F1AP_PDU_t *f1enc = encode_f1ap_cu_configuration_update(&orig);
|
||||||
F1AP_F1AP_PDU_t *f1dec = f1ap_encode_decode(f1enc);
|
F1AP_F1AP_PDU_t *f1dec = f1ap_encode_decode(f1enc);
|
||||||
f1ap_msg_free(f1enc);
|
f1ap_msg_free(f1enc);
|
||||||
|
|||||||
+257
-81
@@ -1083,7 +1083,7 @@ f1ap_gnb_du_system_info_t *get_sys_info(NR_BCCH_BCH_Message_t *mib, const NR_BCC
|
|||||||
nr_SIBs_t *si = (nr_SIBs_t *)seq_arr_at(du_SIBs, i);
|
nr_SIBs_t *si = (nr_SIBs_t *)seq_arr_at(du_SIBs, i);
|
||||||
// other SIB in gNB-DU System Information not implemented yet
|
// other SIB in gNB-DU System Information not implemented yet
|
||||||
// only DU SIB not included in this message is SIB19
|
// only DU SIB not included in this message is SIB19
|
||||||
AssertFatal(si->SIB_type == 19, "Cannot handle SIB%d in gNB-DU System Information\n", si->SIB_type);
|
AssertFatal(si->SIB_type == NR_SIB_19, "Cannot handle SIB%d in gNB-DU System Information\n", (int)si->SIB_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sys_info;
|
return sys_info;
|
||||||
@@ -1243,7 +1243,7 @@ static seq_arr_t *fill_du_sibs(paramdef_t *GNBparamarray)
|
|||||||
int sib_value = GNBparamarray[GNB_DU_SIBS_IDX].iptr[i];
|
int sib_value = GNBparamarray[GNB_DU_SIBS_IDX].iptr[i];
|
||||||
// SIB10, SIB12, SIB13, SIB14, SIB15, SIB17, SIB18, SIB19 and SIB20 are generated by the DU
|
// SIB10, SIB12, SIB13, SIB14, SIB15, SIB17, SIB18, SIB19 and SIB20 are generated by the DU
|
||||||
// all the rest in the DU
|
// all the rest in the DU
|
||||||
AssertFatal((sib_value > 9 && sib_value < 21) && sib_value != 11 && sib_value != 16,
|
AssertFatal((sib_value > NR_SIB_9 && sib_value < NR_SIB_21) && sib_value != NR_SIB_11 && sib_value != NR_SIB_16,
|
||||||
"SIB%d is not a DU SIB, to be configured in DU section\n",
|
"SIB%d is not a DU SIB, to be configured in DU section\n",
|
||||||
sib_value);
|
sib_value);
|
||||||
nr_SIBs_t *du_sib = calloc_or_fail(1, sizeof(nr_SIBs_t));
|
nr_SIBs_t *du_sib = calloc_or_fail(1, sizeof(nr_SIBs_t));
|
||||||
@@ -2012,94 +2012,209 @@ static nr_neighbour_cell_t *get_neighbour_by_pci(seq_arr_t *neighbour_cells, int
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fill_neighbour_cell_configuration(uint8_t gnb_idx, gNB_RRC_INST *rrc)
|
/** @brief Parse gNB-level inter-frequency list (frequency_list) into a shared array.
|
||||||
|
* This parses gNBs.[gnb_idx].frequency_list once and validates per-frequency SIB4
|
||||||
|
* parameters. The resulting list can then be reused for all serving cells.
|
||||||
|
* @param[out] inter_freqs Sequence array of nr_inter_freq_cfg_t to populate
|
||||||
|
* @param[in] gnb_idx gNB index */
|
||||||
|
static bool eq_inter_freq_arfcn_scs(const void *vval, const void *vit)
|
||||||
{
|
{
|
||||||
char gnbpath[MAX_OPTNAME_SIZE + 8];
|
const nr_inter_freq_cfg_t *key = (const nr_inter_freq_cfg_t *)vval;
|
||||||
snprintf(gnbpath, sizeof(gnbpath), "%s.[%i]", GNB_CONFIG_STRING_GNB_LIST, gnb_idx);
|
const nr_inter_freq_cfg_t *cur = (const nr_inter_freq_cfg_t *)vit;
|
||||||
|
return key->arfcn == cur->arfcn && key->scs == cur->scs;
|
||||||
|
}
|
||||||
|
|
||||||
GET_PARAMS_LIST(neighbour_list_param_list,
|
/** @brief Insert an inter-frequency config if (ARFCN,SCS) is unique in the list.
|
||||||
neighbour_list_params,
|
* Fails fast if a duplicate is found.
|
||||||
GNB_NEIGHBOUR_LIST_PARAM_LIST,
|
* @param[in,out] inter_freqs Existing inter-frequency list
|
||||||
GNB_CONFIG_STRING_NEIGHBOUR_LIST,
|
* @param[in] f Candidate entry to insert (by value)
|
||||||
gnbpath);
|
* @param[in] gnb_idx gNB index for logging
|
||||||
|
*/
|
||||||
if (neighbour_list_param_list.numelt < 1)
|
static void add_inter_freq(seq_arr_t *inter_freqs, const nr_inter_freq_cfg_t *f, uint8_t gnb_idx)
|
||||||
return;
|
{
|
||||||
|
const nr_inter_freq_cfg_t key = {.arfcn = f->arfcn, .scs = f->scs};
|
||||||
rrc->neighbour_cell_configuration = malloc(sizeof(seq_arr_t));
|
elm_arr_t dup = find_if(inter_freqs, (void *)&key, eq_inter_freq_arfcn_scs);
|
||||||
seq_arr_init(rrc->neighbour_cell_configuration, sizeof(neighbour_cell_configuration_t));
|
if (dup.found) {
|
||||||
|
AssertFatal(false, "gNB %d: duplicate inter-freq carrier ARFCN %d, SCS %d in frequency_list\n", gnb_idx, f->arfcn, f->scs);
|
||||||
for (int elm = 0; elm < neighbour_list_param_list.numelt; ++elm) {
|
|
||||||
neighbour_cell_configuration_t cell = {0};
|
|
||||||
cell.nr_cell_id = (uint64_t)*neighbour_list_param_list.paramarray[elm][0].u64ptr;
|
|
||||||
|
|
||||||
char neighbourpath[MAX_OPTNAME_SIZE + 8];
|
|
||||||
snprintf(neighbourpath, sizeof(neighbourpath), "%s.[%i].%s.[%i]", GNB_CONFIG_STRING_GNB_LIST, gnb_idx, GNB_CONFIG_STRING_NEIGHBOUR_LIST, elm);
|
|
||||||
GET_PARAMS_LIST(NeighbourCellParamList,
|
|
||||||
NeighbourCellParams,
|
|
||||||
GNBNEIGHBOURCELLPARAMS_DESC,
|
|
||||||
GNB_CONFIG_STRING_NEIGHBOUR_CELL_LIST,
|
|
||||||
neighbourpath);
|
|
||||||
LOG_I(GNB_APP, "Cell %ld has %d neighbours\n", cell.nr_cell_id, NeighbourCellParamList.numelt);
|
|
||||||
if (NeighbourCellParamList.numelt < 1)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
cell.neighbour_cells = malloc_or_fail(sizeof(seq_arr_t));
|
|
||||||
seq_arr_init(cell.neighbour_cells, sizeof(nr_neighbour_cell_t));
|
|
||||||
for (int l = 0; l < NeighbourCellParamList.numelt; ++l) {
|
|
||||||
nr_neighbour_cell_t n = {0};
|
|
||||||
n.gNB_ID = *(NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_GNB_ID_IDX].uptr);
|
|
||||||
n.nrcell_id = (uint64_t) * (NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_NR_CELLID_IDX].u64ptr);
|
|
||||||
n.physicalCellId = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_PHYSICAL_ID_IDX].uptr;
|
|
||||||
n.subcarrierSpacing = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_SCS_IDX].uptr;
|
|
||||||
n.band = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_BAND_IDX].uptr;
|
|
||||||
n.absoluteFrequencySSB = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_ABS_FREQ_SSB_IDX].i64ptr;
|
|
||||||
n.tac = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_TAC_IDX].uptr;
|
|
||||||
|
|
||||||
char neighbour_plmn_path[CONFIG_MAXOPTLENGTH];
|
|
||||||
snprintf(neighbour_plmn_path,
|
|
||||||
sizeof(neighbour_plmn_path),
|
|
||||||
"%s.%s.[%i].%s",
|
|
||||||
neighbourpath,
|
|
||||||
GNB_CONFIG_STRING_NEIGHBOUR_CELL_LIST,
|
|
||||||
l,
|
|
||||||
GNB_CONFIG_STRING_NEIGHBOUR_PLMN);
|
|
||||||
GET_PARAMS(NeighbourPlmn, GNBPLMNPARAMS_DESC, neighbour_plmn_path);
|
|
||||||
|
|
||||||
n.plmn.mcc = *NeighbourPlmn[GNB_MOBILE_COUNTRY_CODE_IDX].uptr;
|
|
||||||
n.plmn.mnc = *NeighbourPlmn[GNB_MOBILE_NETWORK_CODE_IDX].uptr;
|
|
||||||
n.plmn.mnc_digit_length = *NeighbourPlmn[GNB_MNC_DIGIT_LENGTH].uptr;
|
|
||||||
if (get_neighbour_by_pci(cell.neighbour_cells, n.physicalCellId) != NULL) {
|
|
||||||
LOG_E(GNB_APP,
|
|
||||||
"Cell %ld: duplicate PCI %d in neighbour list (nrcell_id %ld)\n",
|
|
||||||
cell.nr_cell_id,
|
|
||||||
n.physicalCellId,
|
|
||||||
n.nrcell_id);
|
|
||||||
AssertFatal(false, "PCI must be unique within a cell's neighbour list\n");
|
|
||||||
}
|
|
||||||
seq_arr_push_back(cell.neighbour_cells, &n, sizeof(n));
|
|
||||||
LOG_I(GNB_APP,
|
|
||||||
" [%d] neighbor ID %d cellId %ld PCI %d SCS %d SSB ARFCN %u TAC %u PLMN %03u.%0*u\n",
|
|
||||||
l,
|
|
||||||
n.gNB_ID,
|
|
||||||
n.nrcell_id,
|
|
||||||
n.physicalCellId,
|
|
||||||
n.subcarrierSpacing,
|
|
||||||
n.absoluteFrequencySSB,
|
|
||||||
n.tac,
|
|
||||||
n.plmn.mcc,
|
|
||||||
n.plmn.mnc_digit_length,
|
|
||||||
n.plmn.mnc);
|
|
||||||
}
|
|
||||||
seq_arr_push_back(rrc->neighbour_cell_configuration, &cell, sizeof(cell));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
seq_arr_push_back(inter_freqs, (void *)f, sizeof(*f));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_inter_freq_list(seq_arr_t *inter_freqs, const uint8_t gnb_idx)
|
||||||
|
{
|
||||||
|
seq_arr_init(inter_freqs, sizeof(nr_inter_freq_cfg_t));
|
||||||
|
|
||||||
|
char freq_path[MAX_OPTNAME_SIZE + 8];
|
||||||
|
snprintf(freq_path, sizeof(freq_path), "%s.[%i]", GNB_CONFIG_STRING_GNB_LIST, gnb_idx);
|
||||||
|
GET_PARAMS_LIST(freq_list, freq_params, GNBFREQUENCYPARAMS_DESC, GNB_CONFIG_STRING_FREQUENCY_LIST, freq_path);
|
||||||
|
|
||||||
|
for (int i = 0; i < freq_list.numelt; ++i) {
|
||||||
|
const paramdef_t *freq_item = freq_list.paramarray[i];
|
||||||
|
nr_inter_freq_cfg_t f = {0};
|
||||||
|
f.arfcn = *gpd(freq_item, sizeofArray(freq_params), GNB_CONFIG_STRING_FREQUENCY_ABS_FREQ_SSB)->i64ptr;
|
||||||
|
f.scs = *gpd(freq_item, sizeofArray(freq_params), GNB_CONFIG_STRING_FREQUENCY_SCS)->uptr;
|
||||||
|
|
||||||
|
char cfg_path[MAX_OPTNAME_SIZE + 16];
|
||||||
|
snprintf(cfg_path,
|
||||||
|
sizeof(cfg_path),
|
||||||
|
"%s.[%i].%s.[%i]",
|
||||||
|
GNB_CONFIG_STRING_GNB_LIST,
|
||||||
|
gnb_idx,
|
||||||
|
GNB_CONFIG_STRING_FREQUENCY_LIST,
|
||||||
|
i);
|
||||||
|
GET_PARAMS(freq_cfg, GNBFREQUENCYCONFIGPARAMS_DESC, cfg_path);
|
||||||
|
|
||||||
|
nr_neighbour_cell_sib4_freq_t *p = &f.freq_cfg;
|
||||||
|
|
||||||
|
p->cellReselectionPriority = *freq_cfg[GNB_CONFIG_FREQUENCY_CELL_RESEL_PRIO_IDX].iptr;
|
||||||
|
p->threshX_HighP = *freq_cfg[GNB_CONFIG_FREQUENCY_THRESH_X_HIGH_P_IDX].iptr;
|
||||||
|
p->threshX_LowP = *freq_cfg[GNB_CONFIG_FREQUENCY_THRESH_X_LOW_P_IDX].iptr;
|
||||||
|
p->threshX_HighQ = *freq_cfg[GNB_CONFIG_FREQUENCY_THRESH_X_HIGH_Q_IDX].iptr;
|
||||||
|
p->threshX_LowQ = *freq_cfg[GNB_CONFIG_FREQUENCY_THRESH_X_LOW_Q_IDX].iptr;
|
||||||
|
p->q_OffsetFreq = *freq_cfg[GNB_CONFIG_FREQUENCY_Q_OFFSET_FREQ_IDX].iptr;
|
||||||
|
f.q_RxLevMin = *freq_cfg[GNB_CONFIG_FREQUENCY_Q_RXLEVMIN_IDX].iptr;
|
||||||
|
f.t_ReselectionNR = *freq_cfg[GNB_CONFIG_FREQUENCY_T_RESEL_NR_IDX].uptr;
|
||||||
|
|
||||||
|
/* Add inter-frequency configuration to the list. */
|
||||||
|
add_inter_freq(inter_freqs, &f, gnb_idx);
|
||||||
|
|
||||||
|
LOG_I(GNB_APP,
|
||||||
|
"gNB %d: SIB4 inter-freq ARFCN %d, SCS %d, prio=%d, ThreshX_HighP=%d, ThreshX_LowP=%d, ThreshX_HighQ=%d, "
|
||||||
|
"ThreshX_LowQ=%d, q_OffsetFreq=%d\n",
|
||||||
|
gnb_idx,
|
||||||
|
f.arfcn,
|
||||||
|
f.scs,
|
||||||
|
f.freq_cfg.cellReselectionPriority,
|
||||||
|
f.freq_cfg.threshX_HighP,
|
||||||
|
f.freq_cfg.threshX_LowP,
|
||||||
|
f.freq_cfg.threshX_HighQ,
|
||||||
|
f.freq_cfg.threshX_LowQ,
|
||||||
|
f.freq_cfg.q_OffsetFreq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_sib3_offsets(nr_neighbour_cell_t *n, const paramdef_t *cell_params, int n_cell_params)
|
||||||
|
{
|
||||||
|
nr_neighbour_cell_neighbor_offset_t *sib3_off = &n->sib3.offset;
|
||||||
|
|
||||||
|
/* SIB3 per-neighbour offsets */
|
||||||
|
sib3_off->q_OffsetCell = *gpd(cell_params, n_cell_params, GNB_CONFIG_STRING_NEIGHBOUR_CELL_Q_OFFSET_CELL)->iptr;
|
||||||
|
sib3_off->q_RxLevMinOffsetCell = *gpd(cell_params, n_cell_params, GNB_CONFIG_STRING_NEIGHBOUR_CELL_Q_RXLEVMIN_OFFSET_CELL)->iptr;
|
||||||
|
sib3_off->q_QualMinOffsetCell = *gpd(cell_params, n_cell_params, GNB_CONFIG_STRING_NEIGHBOUR_CELL_Q_QUALMIN_OFFSET_CELL)->iptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_sib4_offsets(nr_neighbour_cell_t *n, const paramdef_t *cell_params, int n_cell_params)
|
||||||
|
{
|
||||||
|
nr_neighbour_cell_neighbor_offset_t *sib4_off = &n->sib4.offset;
|
||||||
|
|
||||||
|
/* SIB4 per-neighbour offsets */
|
||||||
|
sib4_off->q_OffsetCell = *gpd(cell_params, n_cell_params, GNB_CONFIG_STRING_NEIGHBOUR_CELL_Q_OFFSET_CELL)->iptr;
|
||||||
|
sib4_off->q_RxLevMinOffsetCell = *gpd(cell_params, n_cell_params, GNB_CONFIG_STRING_NEIGHBOUR_CELL_Q_RXLEVMIN_OFFSET_CELL)->iptr;
|
||||||
|
sib4_off->q_QualMinOffsetCell = *gpd(cell_params, n_cell_params, GNB_CONFIG_STRING_NEIGHBOUR_CELL_Q_QUALMIN_OFFSET_CELL)->iptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Parse and add neighbour cells for a given cell configuration
|
||||||
|
* @param[in,out] cell Neighbour cell configuration to populate
|
||||||
|
* @param[in] list Parameter list describing neighbour cells
|
||||||
|
* @param[in] n_path Base path for neighbour configuration in the config tree
|
||||||
|
* @param[in] n_cell_params Number of neighbour-cell parameters */
|
||||||
|
static void parse_neighbour_cells_list(neighbour_cell_configuration_t *cell,
|
||||||
|
const paramlist_def_t *list,
|
||||||
|
const char *n_path,
|
||||||
|
int n_cell_params)
|
||||||
|
{
|
||||||
|
seq_arr_init(&cell->neighbour_cells, sizeof(nr_neighbour_cell_t));
|
||||||
|
|
||||||
|
/* Parse each neighbour cell in the list */
|
||||||
|
for (int l = 0; l < list->numelt; ++l) {
|
||||||
|
const paramdef_t *cell_params = list->paramarray[l];
|
||||||
|
nr_neighbour_cell_t n = {
|
||||||
|
.gNB_ID = *gpd(cell_params, n_cell_params, GNB_CONFIG_STRING_GNB_ID)->uptr,
|
||||||
|
.nrcell_id = *gpd(cell_params, n_cell_params, GNB_CONFIG_STRING_NRCELLID)->u64ptr,
|
||||||
|
.physicalCellId = *gpd(cell_params, n_cell_params, GNB_CONFIG_STRING_NEIGHBOUR_CELL_PHYSICAL_ID)->uptr,
|
||||||
|
.subcarrierSpacing = *gpd(cell_params, n_cell_params, GNB_CONFIG_STRING_NEIGHBOUR_CELL_SCS)->uptr,
|
||||||
|
.band = *gpd(cell_params, n_cell_params, GNB_CONFIG_STRING_NEIGHBOUR_CELL_BAND)->uptr,
|
||||||
|
.absoluteFrequencySSB = *gpd(cell_params, n_cell_params, GNB_CONFIG_STRING_NEIGHBOUR_CELL_ABS_FREQ_SSB)->i64ptr,
|
||||||
|
.tac = *gpd(cell_params, n_cell_params, GNB_CONFIG_STRING_NEIGHBOUR_TRACKING_ARE_CODE)->uptr};
|
||||||
|
|
||||||
|
set_sib3_offsets(&n, cell_params, n_cell_params);
|
||||||
|
set_sib4_offsets(&n, cell_params, n_cell_params);
|
||||||
|
/* SIB4 per-frequency reselection parameters (q_OffsetFreq, thresholds, priority)
|
||||||
|
* are populated in parse_inter_freq_list() per-frequency, not per neighbour here. */
|
||||||
|
|
||||||
|
char p[CONFIG_MAXOPTLENGTH]; // plmn path
|
||||||
|
snprintf(p, sizeof(p), "%s.%s.[%i].%s", n_path, GNB_CONFIG_STRING_NEIGHBOUR_CELL_LIST, l, GNB_CONFIG_STRING_NEIGHBOUR_PLMN);
|
||||||
|
GET_PARAMS(NeighbourPlmn, GNBPLMNPARAMS_DESC, p);
|
||||||
|
n.plmn = extract_plmn_from_params(NeighbourPlmn, sizeofArray(NeighbourPlmn));
|
||||||
|
|
||||||
|
LOG_I(GNB_APP,
|
||||||
|
"Neighbour[%d]: cellId %ld, PLMN %03u.%0*u, gNB %d, PCI %d, SCS %d, SSB ARFCN %u, TAC %u\n",
|
||||||
|
l,
|
||||||
|
n.nrcell_id,
|
||||||
|
n.plmn.mcc,
|
||||||
|
n.plmn.mnc_digit_length,
|
||||||
|
n.plmn.mnc,
|
||||||
|
n.gNB_ID,
|
||||||
|
n.physicalCellId,
|
||||||
|
n.subcarrierSpacing,
|
||||||
|
n.absoluteFrequencySSB,
|
||||||
|
n.tac);
|
||||||
|
|
||||||
|
/* Check for duplicate PCI in the neighbour list */
|
||||||
|
if (get_neighbour_by_pci(&cell->neighbour_cells, n.physicalCellId) != NULL) {
|
||||||
|
LOG_E(GNB_APP, "Cell %ld: duplicate PCI %d in neighbour list (NCI=%ld)\n", cell->nr_cell_id, n.physicalCellId, n.nrcell_id);
|
||||||
|
AssertFatal(false, "PCI must be unique within a cell's neighbour list\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
seq_arr_push_back(&cell->neighbour_cells, &n, sizeof(n));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Sort neighbour cell configuration by cell ID
|
||||||
|
* @param[in] rrc Pointer to RRC instance */
|
||||||
|
static void sort_neighbour_configuration(gNB_RRC_INST *rrc)
|
||||||
|
{
|
||||||
void *base = seq_arr_front(rrc->neighbour_cell_configuration);
|
void *base = seq_arr_front(rrc->neighbour_cell_configuration);
|
||||||
size_t nmemb = seq_arr_size(rrc->neighbour_cell_configuration);
|
size_t nmemb = seq_arr_size(rrc->neighbour_cell_configuration);
|
||||||
size_t element_size = sizeof(neighbour_cell_configuration_t);
|
size_t element_size = sizeof(neighbour_cell_configuration_t);
|
||||||
|
|
||||||
qsort(base, nmemb, element_size, sort_neighbour_cell_config_by_cell_id);
|
qsort(base, nmemb, element_size, sort_neighbour_cell_config_by_cell_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief Fill neighbour cell configuration from config file
|
||||||
|
* @param[in] gnb_idx gNB index
|
||||||
|
* @param[in,out] rrc Pointer to RRC instance */
|
||||||
|
static void fill_neighbour_cell_configuration(const uint8_t gnb_idx, gNB_RRC_INST *rrc)
|
||||||
|
{
|
||||||
|
char path[MAX_OPTNAME_SIZE + 8];
|
||||||
|
snprintf(path, sizeof(path), "%s.[%i]", GNB_CONFIG_STRING_GNB_LIST, gnb_idx);
|
||||||
|
GET_PARAMS_LIST(nlist, nparams, GNB_NEIGHBOUR_LIST_PARAM_LIST, GNB_CONFIG_STRING_NEIGHBOUR_LIST, path);
|
||||||
|
|
||||||
|
LOG_I(GNB_APP, "gNB %d: neighbour_list has %d serving cell(s)\n", gnb_idx, nlist.numelt);
|
||||||
|
if (nlist.numelt < 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Parse gNB-level inter-frequency configuration once and reuse it for all cells. */
|
||||||
|
parse_inter_freq_list(&rrc->inter_freqs, gnb_idx);
|
||||||
|
|
||||||
|
rrc->neighbour_cell_configuration = malloc_or_fail(sizeof(seq_arr_t));
|
||||||
|
seq_arr_init(rrc->neighbour_cell_configuration, sizeof(neighbour_cell_configuration_t));
|
||||||
|
|
||||||
|
for (int elm = 0; elm < nlist.numelt; ++elm) {
|
||||||
|
neighbour_cell_configuration_t cell = {0};
|
||||||
|
cell.nr_cell_id = *gpd(nlist.paramarray[elm], sizeofArray(nparams), GNB_CONFIG_STRING_NRCELLID)->u64ptr;
|
||||||
|
snprintf(path, sizeof(path), "%s.[%i].%s.[%i]", GNB_CONFIG_STRING_GNB_LIST, gnb_idx, GNB_CONFIG_STRING_NEIGHBOUR_LIST, elm);
|
||||||
|
GET_PARAMS_LIST(ncell_list, ncell_params, GNBNEIGHBOURCELLPARAMS_DESC, GNB_CONFIG_STRING_NEIGHBOUR_CELL_LIST, path);
|
||||||
|
LOG_I(GNB_APP, "Cell %ld has %d neighbours configured\n", cell.nr_cell_id, ncell_list.numelt);
|
||||||
|
if (ncell_list.numelt < 1)
|
||||||
|
continue;
|
||||||
|
parse_neighbour_cells_list(&cell, &ncell_list, path, sizeofArray(ncell_params));
|
||||||
|
seq_arr_push_back(rrc->neighbour_cell_configuration, &cell, sizeof(cell));
|
||||||
|
}
|
||||||
|
|
||||||
|
sort_neighbour_configuration(rrc);
|
||||||
|
}
|
||||||
|
|
||||||
static void fill_measurement_configuration(uint8_t gnb_idx, gNB_RRC_INST *rrc)
|
static void fill_measurement_configuration(uint8_t gnb_idx, gNB_RRC_INST *rrc)
|
||||||
{
|
{
|
||||||
char measurement_path[MAX_OPTNAME_SIZE + 8];
|
char measurement_path[MAX_OPTNAME_SIZE + 8];
|
||||||
@@ -2166,6 +2281,64 @@ static void fill_measurement_configuration(uint8_t gnb_idx, gNB_RRC_INST *rrc)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief Parse and populate SIB2 configuration from config file
|
||||||
|
* @param[in] gnb_idx gNB index
|
||||||
|
* @return populated SIB2 configuration */
|
||||||
|
static sib2_config_t fill_sib2_configuration(uint8_t gnb_idx)
|
||||||
|
{
|
||||||
|
sib2_config_t sib2 = {0};
|
||||||
|
|
||||||
|
char sib2_path[MAX_OPTNAME_SIZE * 2 + 8];
|
||||||
|
snprintf(sib2_path, sizeof(sib2_path), "%s.[%i].%s", GNB_CONFIG_STRING_GNB_LIST, gnb_idx, GNB_CONFIG_STRING_SIB2_CONFIG);
|
||||||
|
|
||||||
|
GET_PARAMS(SIB2Params, GNBSIB2PARAMS_DESC, sib2_path);
|
||||||
|
const int n_sib2_params = sizeofArray(SIB2Params);
|
||||||
|
|
||||||
|
cell_reselection_info_common_t *common = &sib2.cell_reselection_info_common;
|
||||||
|
common->q_Hyst = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_Q_HYST)->iptr;
|
||||||
|
/* speedStateReselectionPars */
|
||||||
|
common->speedStateReselectionPars = calloc_or_fail(1, sizeof(*common->speedStateReselectionPars));
|
||||||
|
sib2_speed_state_reselection_pars_t *speed = common->speedStateReselectionPars;
|
||||||
|
|
||||||
|
/* Cell reselection serving frequency information */
|
||||||
|
cell_reselection_serving_freq_info_t *serv = &sib2.cell_reselection_serving_freq_info;
|
||||||
|
serv->cellReselectionPriority = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_CELLRESEL_PRIORITY)->iptr;
|
||||||
|
serv->threshServingLowP = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_THRESH_SERVING_LOW_P)->iptr;
|
||||||
|
serv->threshServingLowQ = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_THRESH_SERVING_LOW_Q)->iptr;
|
||||||
|
serv->s_NonIntraSearchP = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_S_NONINTRASEARCH_P)->iptr;
|
||||||
|
serv->s_NonIntraSearchQ = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_S_NONINTRASEARCH_Q)->iptr;
|
||||||
|
|
||||||
|
/* Intra-frequency cell reselection parameters */
|
||||||
|
intra_freq_cell_reselection_info_t *intra = &sib2.intra_freq_cell_reselection_info;
|
||||||
|
intra->q_RxLevMin = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_Q_RXLEVMIN)->iptr;
|
||||||
|
intra->q_QualMin = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_Q_QUALMIN)->iptr;
|
||||||
|
intra->s_IntraSearchP = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_S_INTRASEARCH_P)->iptr;
|
||||||
|
intra->s_IntraSearchQ = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_S_INTRASEARCH_Q)->iptr;
|
||||||
|
intra->t_ReselectionNR = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_T_RESEL_NR)->iptr;
|
||||||
|
|
||||||
|
/* deriveSSB_IndexFromCell (0/1) */
|
||||||
|
int derive_ssb = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_DERIVE_SSB_IDX)->iptr;
|
||||||
|
sib2.deriveSSB_IndexFromCell = (derive_ssb != 0);
|
||||||
|
|
||||||
|
/* Speed-state pars: optional parameters are omitted from the config file (default applied), not set to -1. */
|
||||||
|
speed->t_Evaluation = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_SPEED_T_EVAL)->iptr;
|
||||||
|
speed->t_HystNormal = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_SPEED_T_HYST_NORMAL)->iptr;
|
||||||
|
speed->n_CellChangeMedium = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_SPEED_N_CELL_CHG_MED)->iptr;
|
||||||
|
speed->n_CellChangeHigh = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_SPEED_N_CELL_CHG_HIGH)->iptr;
|
||||||
|
speed->sf_Medium = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_SPEED_SF_MEDIUM)->iptr;
|
||||||
|
speed->sf_High = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_SPEED_SF_HIGH)->iptr;
|
||||||
|
|
||||||
|
LOG_I(GNB_APP,
|
||||||
|
"gNB %d SIB2 config: q_Hyst=%d dB, cellReselPrio=%d, q_RxLevMin=%d dBm, t_ReselectionNR=%d s\n",
|
||||||
|
gnb_idx,
|
||||||
|
common->q_Hyst,
|
||||||
|
serv->cellReselectionPriority,
|
||||||
|
intra->q_RxLevMin,
|
||||||
|
intra->t_ReselectionNR);
|
||||||
|
|
||||||
|
return sib2;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Allocates and initializes RRC instances
|
* @brief Allocates and initializes RRC instances
|
||||||
* Currently assuming 1 instance
|
* Currently assuming 1 instance
|
||||||
@@ -2265,6 +2438,9 @@ gNB_RRC_INST *RCconfig_NRRRC()
|
|||||||
}//
|
}//
|
||||||
}//End for (k=0; k <num_gnbs ; k++)
|
}//End for (k=0; k <num_gnbs ; k++)
|
||||||
openair_rrc_gNB_configuration(rrc, &nrrrc_config);
|
openair_rrc_gNB_configuration(rrc, &nrrrc_config);
|
||||||
|
|
||||||
|
/* Load SIB2 configuration */
|
||||||
|
rrc->sib2_config = fill_sib2_configuration(i);
|
||||||
}//End if (num_gnbs>0)
|
}//End if (num_gnbs>0)
|
||||||
|
|
||||||
if (!NODE_IS_DU(rrc->node_type))
|
if (!NODE_IS_DU(rrc->node_type))
|
||||||
|
|||||||
@@ -31,19 +31,28 @@ uint16_t set_snssai_config(nssai_t *nssai, const int max_num_ssi, uint8_t k, uin
|
|||||||
return num_ssi;
|
return num_ssi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief Extract PLMN from parameter array
|
||||||
|
* @param[in] params Parameter array containing PLMN fields
|
||||||
|
* @return plmn_id_t structure filled from parameters */
|
||||||
|
plmn_id_t extract_plmn_from_params(const paramdef_t *params, int n_params)
|
||||||
|
{
|
||||||
|
plmn_id_t plmn = {.mcc = *gpd(params, n_params, GNB_CONFIG_STRING_MOBILE_COUNTRY_CODE)->uptr,
|
||||||
|
.mnc = *gpd(params, n_params, GNB_CONFIG_STRING_MOBILE_NETWORK_CODE)->uptr,
|
||||||
|
.mnc_digit_length = *gpd(params, n_params, GNB_CONFIG_STRING_MNC_DIGIT_LENGTH)->uptr};
|
||||||
|
AssertFatal((plmn.mnc_digit_length == 2) || (plmn.mnc_digit_length == 3), "BAD MNC DIGIT LENGTH %d", plmn.mnc_digit_length);
|
||||||
|
return plmn;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t set_plmn_config(plmn_id_t *p, uint8_t idx)
|
uint8_t set_plmn_config(plmn_id_t *p, uint8_t idx)
|
||||||
{
|
{
|
||||||
char gnbpath[MAX_OPTNAME_SIZE * 2 + 8];
|
char gnbpath[MAX_OPTNAME_SIZE * 2 + 8];
|
||||||
snprintf(gnbpath, sizeof(gnbpath), "%s.[%i]", GNB_CONFIG_STRING_GNB_LIST, idx);
|
snprintf(gnbpath, sizeof(gnbpath), "%s.[%i]", GNB_CONFIG_STRING_GNB_LIST, idx);
|
||||||
GET_PARAMS_LIST(PLMNParamList, PLMNParams, GNBPLMNPARAMS_DESC, GNB_CONFIG_STRING_PLMN_LIST, gnbpath, PLMNPARAMS_CHECK);
|
GET_PARAMS_LIST(PLMNParamList, PLMNParams, GNBPLMNPARAMS_DESC, GNB_CONFIG_STRING_PLMN_LIST, gnbpath, PLMNPARAMS_CHECK);
|
||||||
uint8_t num_plmn = PLMNParamList.numelt;
|
uint8_t num_plmn = PLMNParamList.numelt;
|
||||||
|
const int n_plmn_params = sizeofArray(PLMNParams);
|
||||||
AssertFatal(num_plmn >= 1 && num_plmn <= 6, "The number of PLMN IDs must be in [1,6], but is %d\n", num_plmn);
|
AssertFatal(num_plmn >= 1 && num_plmn <= 6, "The number of PLMN IDs must be in [1,6], but is %d\n", num_plmn);
|
||||||
for (int l = 0; l < num_plmn; ++l) {
|
for (int l = 0; l < num_plmn; ++l) {
|
||||||
plmn_id_t *plmn = &p[l];
|
p[l] = extract_plmn_from_params(PLMNParamList.paramarray[l], n_plmn_params);
|
||||||
plmn->mcc = *PLMNParamList.paramarray[l][GNB_MOBILE_COUNTRY_CODE_IDX].uptr;
|
|
||||||
plmn->mnc = *PLMNParamList.paramarray[l][GNB_MOBILE_NETWORK_CODE_IDX].uptr;
|
|
||||||
plmn->mnc_digit_length = *PLMNParamList.paramarray[l][GNB_MNC_DIGIT_LENGTH].u8ptr;
|
|
||||||
AssertFatal((plmn->mnc_digit_length == 2) || (plmn->mnc_digit_length == 3), "BAD MNC DIGIT LENGTH %d", plmn->mnc_digit_length);
|
|
||||||
}
|
}
|
||||||
return num_plmn;
|
return num_plmn;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include "common/5g_platform_types.h"
|
#include "common/5g_platform_types.h"
|
||||||
|
#include "common/config/config_paramdesc.h"
|
||||||
|
|
||||||
uint16_t set_snssai_config(nssai_t *nssai, const int max_num_ssi, uint8_t k, uint8_t l);
|
uint16_t set_snssai_config(nssai_t *nssai, const int max_num_ssi, uint8_t k, uint8_t l);
|
||||||
uint8_t set_plmn_config(plmn_id_t *p, uint8_t idx);
|
uint8_t set_plmn_config(plmn_id_t *p, uint8_t idx);
|
||||||
|
plmn_id_t extract_plmn_from_params(const paramdef_t *params, int n_params);
|
||||||
|
|||||||
+190
-17
@@ -10,6 +10,7 @@
|
|||||||
#define __GNB_APP_GNB_PARAMDEF__H__
|
#define __GNB_APP_GNB_PARAMDEF__H__
|
||||||
|
|
||||||
#include "common/config/config_paramdesc.h"
|
#include "common/config/config_paramdesc.h"
|
||||||
|
#include "common/config/config_userapi.h"
|
||||||
#include "common/ngran_types.h"
|
#include "common/ngran_types.h"
|
||||||
#include "RRC_nr_paramsvalues.h"
|
#include "RRC_nr_paramsvalues.h"
|
||||||
|
|
||||||
@@ -289,7 +290,6 @@ typedef enum {
|
|||||||
#define GNB_NEIGHBOUR_LIST_PARAM_LIST { \
|
#define GNB_NEIGHBOUR_LIST_PARAM_LIST { \
|
||||||
/* optname helpstr paramflags XXXptr def val type numelt */ \
|
/* optname helpstr paramflags XXXptr def val type numelt */ \
|
||||||
{GNB_CONFIG_STRING_NRCELLID, "cell nrCell Id which has neighbours", PARAMFLAG_MANDATORY, .u64ptr=NULL, .defint64val=0, TYPE_UINT64, 0}, \
|
{GNB_CONFIG_STRING_NRCELLID, "cell nrCell Id which has neighbours", PARAMFLAG_MANDATORY, .u64ptr=NULL, .defint64val=0, TYPE_UINT64, 0}, \
|
||||||
{GNB_CONFIG_STRING_NEIGHBOUR_CELL_PHYSICAL_ID, "neighbour cell physical id", PARAMFLAG_MANDATORY, .uptr=NULL, .defuintval=0, TYPE_UINT, 0}, \
|
|
||||||
}
|
}
|
||||||
// clang-format on
|
// clang-format on
|
||||||
#define GNB_CONFIG_STRING_NEIGHBOUR_CELL_LIST "neighbour_cell_configuration"
|
#define GNB_CONFIG_STRING_NEIGHBOUR_CELL_LIST "neighbour_cell_configuration"
|
||||||
@@ -300,27 +300,111 @@ typedef enum {
|
|||||||
#define GNB_CONFIG_STRING_NEIGHBOUR_CELL_BAND "band"
|
#define GNB_CONFIG_STRING_NEIGHBOUR_CELL_BAND "band"
|
||||||
#define GNB_CONFIG_STRING_NEIGHBOUR_TRACKING_ARE_CODE "tracking_area_code"
|
#define GNB_CONFIG_STRING_NEIGHBOUR_TRACKING_ARE_CODE "tracking_area_code"
|
||||||
#define GNB_CONFIG_STRING_NEIGHBOUR_PLMN "plmn"
|
#define GNB_CONFIG_STRING_NEIGHBOUR_PLMN "plmn"
|
||||||
|
/* SIB3 fields (intra-frequency) - per-neighbor */
|
||||||
|
#define GNB_CONFIG_STRING_NEIGHBOUR_CELL_Q_OFFSET_CELL "q_OffsetCell"
|
||||||
|
#define GNB_CONFIG_STRING_NEIGHBOUR_CELL_Q_RXLEVMIN_OFFSET_CELL "q_RxLevMinOffsetCell"
|
||||||
|
#define GNB_CONFIG_STRING_NEIGHBOUR_CELL_Q_QUALMIN_OFFSET_CELL "q_QualMinOffsetCell"
|
||||||
|
/* SIB4 per-frequency fields are configured in frequency_list.frequency_config */
|
||||||
|
|
||||||
#define GNB_CONFIG_N_CELL_GNB_ID_IDX 0
|
|
||||||
#define GNB_CONFIG_N_CELL_NR_CELLID_IDX 1
|
|
||||||
#define GNB_CONFIG_N_CELL_PHYSICAL_ID_IDX 2
|
|
||||||
#define GNB_CONFIG_N_CELL_ABS_FREQ_SSB_IDX 3
|
|
||||||
#define GNB_CONFIG_N_CELL_SCS_IDX 4
|
|
||||||
#define GNB_CONFIG_N_CELL_BAND_IDX 5
|
|
||||||
#define GNB_CONFIG_N_CELL_TAC_IDX 6
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
#define GNBNEIGHBOURCELLPARAMS_DESC { \
|
#define GNBNEIGHBOURCELLPARAMS_DESC { /* optname helpstr paramflags XXXptr def val type numelt */ \
|
||||||
/* optname helpstr paramflags XXXptr def val type numelt */ \
|
{GNB_CONFIG_STRING_GNB_ID, \
|
||||||
{GNB_CONFIG_STRING_GNB_ID, "neighbour cell's gNB ID", PARAMFLAG_MANDATORY, .uptr=NULL, .defintval=0, TYPE_UINT, 0}, \
|
"neighbour cell's gNB ID", PARAMFLAG_MANDATORY, .uptr=NULL, .defintval=0, TYPE_UINT, 0, \
|
||||||
{GNB_CONFIG_STRING_NRCELLID, "neighbour cell nrCell Id", PARAMFLAG_MANDATORY, .u64ptr=NULL, .defint64val=0, TYPE_UINT64, 0}, \
|
.chkPptr=NULL}, \
|
||||||
{GNB_CONFIG_STRING_NEIGHBOUR_CELL_PHYSICAL_ID, "neighbour cell physical id", PARAMFLAG_MANDATORY, .uptr=NULL, .defuintval=0, TYPE_UINT, 0}, \
|
{GNB_CONFIG_STRING_NRCELLID, \
|
||||||
{GNB_CONFIG_STRING_NEIGHBOUR_CELL_ABS_FREQ_SSB, "neighbour cell abs freq ssb", PARAMFLAG_MANDATORY, .i64ptr=NULL, .defint64val=0, TYPE_INT64, 0}, \
|
"neighbour cell nrCell Id", PARAMFLAG_MANDATORY, .u64ptr=NULL, .defint64val=0, TYPE_UINT64, 0, \
|
||||||
{GNB_CONFIG_STRING_NEIGHBOUR_CELL_SCS, "neighbour cell scs", PARAMFLAG_MANDATORY, .uptr=NULL, .defuintval=0, TYPE_UINT, 0}, \
|
.chkPptr=NULL}, \
|
||||||
{GNB_CONFIG_STRING_NEIGHBOUR_CELL_BAND, "neighbour cell band", PARAMFLAG_MANDATORY, .uptr=NULL, .defuintval=78, TYPE_UINT, 0}, \
|
{GNB_CONFIG_STRING_NEIGHBOUR_CELL_PHYSICAL_ID, \
|
||||||
{GNB_CONFIG_STRING_NEIGHBOUR_TRACKING_ARE_CODE, "neighbour cell tracking area", PARAMFLAG_MANDATORY, .uptr=NULL, .defuintval=0, TYPE_UINT, 0}, \
|
"neighbour cell physical id", PARAMFLAG_MANDATORY, .uptr=NULL, .defuintval=0, TYPE_UINT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s2 = {config_check_uintrange, {0, 1007}}}}, \
|
||||||
|
{GNB_CONFIG_STRING_NEIGHBOUR_CELL_ABS_FREQ_SSB, \
|
||||||
|
"neighbour cell absolute frequency SSB (0..3279165)", PARAMFLAG_MANDATORY, .i64ptr=NULL, .defint64val=0, TYPE_INT64, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 3279165}}}}, \
|
||||||
|
{GNB_CONFIG_STRING_NEIGHBOUR_CELL_SCS, \
|
||||||
|
"neighbour cell scs (15/30/60/120 kHz)", PARAMFLAG_MANDATORY, .uptr=NULL, .defuintval=0, TYPE_UINT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s1 = {config_check_intval, {0, 1, 2, 3}, 4}}}, \
|
||||||
|
{GNB_CONFIG_STRING_NEIGHBOUR_CELL_BAND, \
|
||||||
|
"neighbour cell band (1..1024)", PARAMFLAG_MANDATORY, .uptr=NULL, .defuintval=78, TYPE_UINT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s2 = {config_check_uintrange, {1, 1024}}}}, \
|
||||||
|
{GNB_CONFIG_STRING_NEIGHBOUR_TRACKING_ARE_CODE, \
|
||||||
|
"neighbour cell tracking area (0..16777215)", PARAMFLAG_MANDATORY, .uptr=NULL, .defuintval=0, TYPE_UINT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s2 = {config_check_uintrange, {0, 16777215}}}}, \
|
||||||
|
{GNB_CONFIG_STRING_NEIGHBOUR_CELL_Q_OFFSET_CELL, \
|
||||||
|
"Q-OffsetCell for SIB3/SIB4 (Q-OffsetRange, default 0)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s1 = {config_check_intval, \
|
||||||
|
{-24, -22, -20, -18, -16, -14, -12, -10, -8, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24}, 31}}}, \
|
||||||
|
{GNB_CONFIG_STRING_NEIGHBOUR_CELL_Q_RXLEVMIN_OFFSET_CELL, \
|
||||||
|
"Q-RxLevMinOffsetCell for SIB3/SIB4 (1..8 or -1=disabled)", 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s1 = {config_check_intval, {-1, 1, 2, 3, 4, 5, 6, 7, 8}, 9}}}, \
|
||||||
|
{GNB_CONFIG_STRING_NEIGHBOUR_CELL_Q_QUALMIN_OFFSET_CELL, \
|
||||||
|
"Q-QualMinOffsetCell for SIB3/SIB4 (1..8 or -1=disabled)", 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s1 = {config_check_intval, {-1, 1, 2, 3, 4, 5, 6, 7, 8}, 9}}}, \
|
||||||
}
|
}
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
/* Per-frequency neighbour configuration (frequency_list) */
|
||||||
|
#define GNB_CONFIG_STRING_FREQUENCY_LIST "frequency_list"
|
||||||
|
#define GNB_CONFIG_STRING_FREQUENCY_ABS_FREQ_SSB "absoluteFrequencySSB"
|
||||||
|
#define GNB_CONFIG_STRING_FREQUENCY_SCS "subcarrierSpacing"
|
||||||
|
#define GNB_CONFIG_STRING_FREQUENCY_BAND "band"
|
||||||
|
#define GNB_CONFIG_STRING_FREQUENCY_CONFIG "frequency_config"
|
||||||
|
|
||||||
|
#define GNB_CONFIG_STRING_FREQUENCY_CELL_RESEL_PRIO "cellReselectionPriority"
|
||||||
|
#define GNB_CONFIG_STRING_FREQUENCY_THRESH_X_HIGH_P "threshX_HighP"
|
||||||
|
#define GNB_CONFIG_STRING_FREQUENCY_THRESH_X_LOW_P "threshX_LowP"
|
||||||
|
#define GNB_CONFIG_STRING_FREQUENCY_THRESH_X_HIGH_Q "threshX_HighQ"
|
||||||
|
#define GNB_CONFIG_STRING_FREQUENCY_THRESH_X_LOW_Q "threshX_LowQ"
|
||||||
|
#define GNB_CONFIG_STRING_FREQUENCY_Q_OFFSET_FREQ "q_OffsetFreq"
|
||||||
|
#define GNB_CONFIG_STRING_FREQUENCY_Q_RXLEVMIN "q_RxLevMin"
|
||||||
|
#define GNB_CONFIG_STRING_FREQUENCY_T_RESEL_NR "t_ReselectionNR"
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
#define GNBFREQUENCYPARAMS_DESC { \
|
||||||
|
/* optname helpstr paramflags XXXptr def val type numelt */ \
|
||||||
|
{GNB_CONFIG_STRING_FREQUENCY_ABS_FREQ_SSB, "inter-frequency abs freq ssb", PARAMFLAG_MANDATORY, .i64ptr=NULL, .defint64val=0, TYPE_INT64, 0}, \
|
||||||
|
{GNB_CONFIG_STRING_FREQUENCY_SCS, "inter-frequency scs", PARAMFLAG_MANDATORY, .uptr=NULL, .defuintval=0, TYPE_UINT, 0}, \
|
||||||
|
{GNB_CONFIG_STRING_FREQUENCY_BAND, "inter-frequency band", PARAMFLAG_MANDATORY, .uptr=NULL, .defuintval=78, TYPE_UINT, 0}, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GNBFREQUENCYCONFIGPARAMS_DESC { \
|
||||||
|
{GNB_CONFIG_STRING_FREQUENCY_CELL_RESEL_PRIO, \
|
||||||
|
"SIB4 cellReselectionPriority (0..7, -1=disabled)", 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {-1, 7}}}}, \
|
||||||
|
{GNB_CONFIG_STRING_FREQUENCY_THRESH_X_HIGH_P, \
|
||||||
|
"SIB4 threshX-HighP (0..31)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 31}}}}, \
|
||||||
|
{GNB_CONFIG_STRING_FREQUENCY_THRESH_X_LOW_P, \
|
||||||
|
"SIB4 threshX-LowP (0..31)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 31}}}}, \
|
||||||
|
{GNB_CONFIG_STRING_FREQUENCY_THRESH_X_HIGH_Q, \
|
||||||
|
"SIB4 threshX-HighQ (0..31, -1=disabled)", 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {-1, 31}}}}, \
|
||||||
|
{GNB_CONFIG_STRING_FREQUENCY_THRESH_X_LOW_Q, \
|
||||||
|
"SIB4 threshX-LowQ (0..31, -1=disabled)", 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {-1, 31}}}}, \
|
||||||
|
{GNB_CONFIG_STRING_FREQUENCY_Q_OFFSET_FREQ, \
|
||||||
|
"SIB4 q-OffsetFreq (Q-OffsetRange values, default 0)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s1 = {config_check_intval, {-24, -22, -20, -18, -16, -14, -12, -10, \
|
||||||
|
-8, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24}, 31}}}, \
|
||||||
|
{GNB_CONFIG_STRING_FREQUENCY_Q_RXLEVMIN, \
|
||||||
|
"SIB4 per-carrier Q-RxLevMin (-70..-22, default -22)", 0, .iptr=NULL, .defintval=-22, TYPE_INT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {-70, -22}}}}, \
|
||||||
|
{GNB_CONFIG_STRING_FREQUENCY_T_RESEL_NR, \
|
||||||
|
"SIB4 per-carrier t-ReselectionNR (0..7, default 0)", 0, .uptr=NULL, .defuintval=0, TYPE_UINT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s2 = {config_check_uintrange, {0, 7}}}}, \
|
||||||
|
}
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
/* Indexes into GNBFREQUENCYCONFIGPARAMS_DESC */
|
||||||
|
#define GNB_CONFIG_FREQUENCY_CELL_RESEL_PRIO_IDX 0
|
||||||
|
#define GNB_CONFIG_FREQUENCY_THRESH_X_HIGH_P_IDX 1
|
||||||
|
#define GNB_CONFIG_FREQUENCY_THRESH_X_LOW_P_IDX 2
|
||||||
|
#define GNB_CONFIG_FREQUENCY_THRESH_X_HIGH_Q_IDX 3
|
||||||
|
#define GNB_CONFIG_FREQUENCY_THRESH_X_LOW_Q_IDX 4
|
||||||
|
#define GNB_CONFIG_FREQUENCY_Q_OFFSET_FREQ_IDX 5
|
||||||
|
#define GNB_CONFIG_FREQUENCY_Q_RXLEVMIN_IDX 6
|
||||||
|
#define GNB_CONFIG_FREQUENCY_T_RESEL_NR_IDX 7
|
||||||
|
|
||||||
/* New Measurement Configurations*/
|
/* New Measurement Configurations*/
|
||||||
|
|
||||||
#define GNB_CONFIG_STRING_MEASUREMENT_CONFIGURATION "nr_measurement_configuration"
|
#define GNB_CONFIG_STRING_MEASUREMENT_CONFIGURATION "nr_measurement_configuration"
|
||||||
@@ -369,6 +453,95 @@ typedef enum {
|
|||||||
#define MEASUREMENT_EVENTS_INCLUDE_BEAM_MEAS_IDX 1
|
#define MEASUREMENT_EVENTS_INCLUDE_BEAM_MEAS_IDX 1
|
||||||
#define MEASUREMENT_EVENTS_MAX_RS_INDEX_TO_REPORT 2
|
#define MEASUREMENT_EVENTS_MAX_RS_INDEX_TO_REPORT 2
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------------------------------------------------------------------*/
|
||||||
|
/* SIB2 cell reselection configuration parameters */
|
||||||
|
/*-------------------------------------------------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#define GNB_CONFIG_STRING_SIB2_CONFIG "sib2_config"
|
||||||
|
|
||||||
|
/* clang-format off */
|
||||||
|
|
||||||
|
/* SIB2 top-level parameters (per cell) */
|
||||||
|
#define GNB_CONFIG_STRING_SIB2_Q_HYST "q_Hyst"
|
||||||
|
#define GNB_CONFIG_STRING_SIB2_CELLRESEL_PRIORITY "cellReselectionPriority"
|
||||||
|
#define GNB_CONFIG_STRING_SIB2_THRESH_SERVING_LOW_P "threshServingLowP"
|
||||||
|
#define GNB_CONFIG_STRING_SIB2_THRESH_SERVING_LOW_Q "threshServingLowQ"
|
||||||
|
#define GNB_CONFIG_STRING_SIB2_S_NONINTRASEARCH_P "s_NonIntraSearchP"
|
||||||
|
#define GNB_CONFIG_STRING_SIB2_S_NONINTRASEARCH_Q "s_NonIntraSearchQ"
|
||||||
|
#define GNB_CONFIG_STRING_SIB2_Q_RXLEVMIN "q_RxLevMin"
|
||||||
|
#define GNB_CONFIG_STRING_SIB2_Q_QUALMIN "q_QualMin"
|
||||||
|
#define GNB_CONFIG_STRING_SIB2_S_INTRASEARCH_P "s_IntraSearchP"
|
||||||
|
#define GNB_CONFIG_STRING_SIB2_S_INTRASEARCH_Q "s_IntraSearchQ"
|
||||||
|
#define GNB_CONFIG_STRING_SIB2_T_RESEL_NR "t_ReselectionNR"
|
||||||
|
#define GNB_CONFIG_STRING_SIB2_DERIVE_SSB_IDX "deriveSSB_IndexFromCell"
|
||||||
|
|
||||||
|
/* SIB2 speedStateReselectionPars parameters */
|
||||||
|
#define GNB_CONFIG_STRING_SIB2_SPEED_T_EVAL "speed_t_Evaluation"
|
||||||
|
#define GNB_CONFIG_STRING_SIB2_SPEED_T_HYST_NORMAL "speed_t_HystNormal"
|
||||||
|
#define GNB_CONFIG_STRING_SIB2_SPEED_N_CELL_CHG_MED "speed_n_CellChangeMedium"
|
||||||
|
#define GNB_CONFIG_STRING_SIB2_SPEED_N_CELL_CHG_HIGH "speed_n_CellChangeHigh"
|
||||||
|
#define GNB_CONFIG_STRING_SIB2_SPEED_SF_MEDIUM "speed_sf_Medium"
|
||||||
|
#define GNB_CONFIG_STRING_SIB2_SPEED_SF_HIGH "speed_sf_High"
|
||||||
|
|
||||||
|
#define GNBSIB2PARAMS_DESC { /* optname helpstr paramflags XXXptr def val type numelt */ \
|
||||||
|
{GNB_CONFIG_STRING_SIB2_Q_HYST, \
|
||||||
|
"SIB2 q-Hyst (0,1,2,3,4,5,6,8,10..24 dB)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s1 = {config_check_intval, {0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24}, 16}}}, \
|
||||||
|
{GNB_CONFIG_STRING_SIB2_CELLRESEL_PRIORITY, \
|
||||||
|
"SIB2 cellReselectionPriority (0..7)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 7}}}}, \
|
||||||
|
{GNB_CONFIG_STRING_SIB2_THRESH_SERVING_LOW_P, \
|
||||||
|
"SIB2 threshServingLowP (0..31)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 31}}}}, \
|
||||||
|
{GNB_CONFIG_STRING_SIB2_THRESH_SERVING_LOW_Q, \
|
||||||
|
"SIB2 threshServingLowQ (0..31, -1=disabled)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {-1, 31}}}}, \
|
||||||
|
{GNB_CONFIG_STRING_SIB2_S_NONINTRASEARCH_P, \
|
||||||
|
"SIB2 s-NonIntraSearchP (0..31, -1=disabled)", 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {-1, 31}}}}, \
|
||||||
|
{GNB_CONFIG_STRING_SIB2_S_NONINTRASEARCH_Q, \
|
||||||
|
"SIB2 s-NonIntraSearchQ (0..31, -1=disabled)", 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {-1, 31}}}}, \
|
||||||
|
{GNB_CONFIG_STRING_SIB2_Q_RXLEVMIN, \
|
||||||
|
"SIB2 q-RxLevMin (-70..-22)", 0, .iptr=NULL, .defintval=-56, TYPE_INT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {-70, -22}}}}, \
|
||||||
|
{GNB_CONFIG_STRING_SIB2_Q_QUALMIN, \
|
||||||
|
"SIB2 q-QualMin (-43..-12 or -1=disabled)", 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s1 = {config_check_intval, \
|
||||||
|
{-1, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, \
|
||||||
|
-27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12}, 33}}}, \
|
||||||
|
{GNB_CONFIG_STRING_SIB2_S_INTRASEARCH_P, \
|
||||||
|
"SIB2 s-IntraSearchP (0..31)", 0, .iptr=NULL, .defintval=22, TYPE_INT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 31}}}}, \
|
||||||
|
{GNB_CONFIG_STRING_SIB2_S_INTRASEARCH_Q, \
|
||||||
|
"SIB2 s-IntraSearchQ (0..31, -1=disabled)", 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {-1, 31}}}}, \
|
||||||
|
{GNB_CONFIG_STRING_SIB2_T_RESEL_NR, \
|
||||||
|
"SIB2 t-ReselectionNR (0..7)", 0, .iptr=NULL, .defintval=1, TYPE_INT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 7}}}}, \
|
||||||
|
{GNB_CONFIG_STRING_SIB2_DERIVE_SSB_IDX, \
|
||||||
|
"SIB2 deriveSSB-IndexFromCell (0/1)", PARAMFLAG_BOOL, .iptr=NULL, .defintval=1, TYPE_INT, 0}, \
|
||||||
|
{GNB_CONFIG_STRING_SIB2_SPEED_T_EVAL, \
|
||||||
|
"SIB2 speed t-Evaluation (0=s30,1=s60,2=s120,3=s180,4=s240)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 4}}}}, \
|
||||||
|
{GNB_CONFIG_STRING_SIB2_SPEED_T_HYST_NORMAL, \
|
||||||
|
"SIB2 speed t-HystNormal (0=s30,1=s60,2=s120,3=s180,4=s240)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 4}}}}, \
|
||||||
|
{GNB_CONFIG_STRING_SIB2_SPEED_N_CELL_CHG_MED, \
|
||||||
|
"SIB2 speed n-CellChangeMedium (1..16)", 0, .iptr=NULL, .defintval=1, TYPE_INT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {1, 16}}}}, \
|
||||||
|
{GNB_CONFIG_STRING_SIB2_SPEED_N_CELL_CHG_HIGH, \
|
||||||
|
"SIB2 speed n-CellChangeHigh (1..16)", 0, .iptr=NULL, .defintval=2, TYPE_INT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {1, 16}}}}, \
|
||||||
|
{GNB_CONFIG_STRING_SIB2_SPEED_SF_MEDIUM, \
|
||||||
|
"SIB2 speed sf-Medium (0=dB-6,1=dB-4,2=dB-2,3=dB0)", 0, .iptr=NULL, .defintval=1, TYPE_INT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 3}}}}, \
|
||||||
|
{GNB_CONFIG_STRING_SIB2_SPEED_SF_HIGH, \
|
||||||
|
"SIB2 speed sf-High (0=dB-6,1=dB-4,2=dB-2,3=dB0)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \
|
||||||
|
.chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 3}}}}, \
|
||||||
|
}
|
||||||
|
/* clang-format on */
|
||||||
|
|
||||||
/* PLMN ID configuration */
|
/* PLMN ID configuration */
|
||||||
|
|
||||||
#define GNB_CONFIG_STRING_PLMN_LIST "plmn_list"
|
#define GNB_CONFIG_STRING_PLMN_LIST "plmn_list"
|
||||||
|
|||||||
@@ -988,27 +988,56 @@ bool nr_mac_configure_other_sib(gNB_MAC_INST *nrmac, int num_cu_sib, const f1ap_
|
|||||||
NR_SystemInformation_IEs_t *sysInfov17 = calloc(1, sizeof(*sysInfov17)); // for othersibs
|
NR_SystemInformation_IEs_t *sysInfov17 = calloc(1, sizeof(*sysInfov17)); // for othersibs
|
||||||
for (int i = 0; i < num_cu_sib; i++) {
|
for (int i = 0; i < num_cu_sib; i++) {
|
||||||
config_sibs[i] = cu_sib[i].SI_type;
|
config_sibs[i] = cu_sib[i].SI_type;
|
||||||
|
const byte_array_t *container = &cu_sib[i].SI_container;
|
||||||
|
if (!container->buf || container->len == 0) {
|
||||||
|
LOG_W(NR_MAC, "SIB%d container is invalid (ptr=%p len=%zu), skipping\n", config_sibs[i], container->buf, container->len);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
switch (config_sibs[i]) {
|
switch (config_sibs[i]) {
|
||||||
case 2: {
|
case NR_SIB_2: {
|
||||||
struct NR_SystemInformation_IEs__sib_TypeAndInfo__Member *type = calloc(1, sizeof(*type));
|
struct NR_SystemInformation_IEs__sib_TypeAndInfo__Member *type = calloc(1, sizeof(*type));
|
||||||
type->present = NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib2;
|
type->present = NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib2;
|
||||||
// SIB2 coming from CU need to decode it
|
// SIB2 coming from CU need to decode it
|
||||||
NR_SIB2_t *sib2 = NULL;
|
NR_SIB2_t *sib2 = NULL;
|
||||||
asn_dec_rval_t dec_rval = uper_decode(NULL,
|
asn_dec_rval_t dec_rval = uper_decode(NULL, &asn_DEF_NR_SIB2, (void **)&sib2, container->buf, container->len, 0, 0);
|
||||||
&asn_DEF_NR_SIB2,
|
|
||||||
(void **)&sib2,
|
|
||||||
cu_sib[i].SI_container,
|
|
||||||
cu_sib[i].SI_container_length,
|
|
||||||
0,
|
|
||||||
0);
|
|
||||||
if (dec_rval.code != RC_OK) {
|
if (dec_rval.code != RC_OK) {
|
||||||
LOG_E(NR_MAC, "cannot decode SIB%d from CU\n", config_sibs[i]);
|
LOG_E(NR_MAC, "cannot decode SIB%d from CU\n", config_sibs[i]);
|
||||||
ASN_STRUCT_FREE(asn_DEF_NR_SIB2, cu_sib[i].SI_container);
|
ASN_STRUCT_FREE(asn_DEF_NR_SIB2, sib2);
|
||||||
}
|
}
|
||||||
type->choice.sib2 = sib2;
|
type->choice.sib2 = sib2;
|
||||||
add_sib_to_systeminformation(sysInfo, type);
|
add_sib_to_systeminformation(sysInfo, type);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case NR_SIB_3: {
|
||||||
|
struct NR_SystemInformation_IEs__sib_TypeAndInfo__Member *type = calloc_or_fail(1, sizeof(*type));
|
||||||
|
type->present = NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib3;
|
||||||
|
NR_SIB3_t *sib3 = NULL;
|
||||||
|
asn_dec_rval_t dec_rval = uper_decode(NULL, &asn_DEF_NR_SIB3, (void **)&sib3, container->buf, container->len, 0, 0);
|
||||||
|
if (dec_rval.code != RC_OK) {
|
||||||
|
LOG_E(NR_MAC, "cannot decode SIB%d from CU\n", config_sibs[i]);
|
||||||
|
ASN_STRUCT_FREE(asn_DEF_NR_SIB3, sib3);
|
||||||
|
free(type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
type->choice.sib3 = sib3;
|
||||||
|
add_sib_to_systeminformation(sysInfo, type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NR_SIB_4: {
|
||||||
|
struct NR_SystemInformation_IEs__sib_TypeAndInfo__Member *type = calloc_or_fail(1, sizeof(*type));
|
||||||
|
type->present = NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib4;
|
||||||
|
NR_SIB4_t *sib4 = NULL;
|
||||||
|
asn_dec_rval_t dec_rval = uper_decode(NULL, &asn_DEF_NR_SIB4, (void **)&sib4, container->buf, container->len, 0, 0);
|
||||||
|
if (dec_rval.code != RC_OK) {
|
||||||
|
LOG_E(NR_MAC, "cannot decode SIB%d from CU\n", config_sibs[i]);
|
||||||
|
ASN_STRUCT_FREE(asn_DEF_NR_SIB4, sib4);
|
||||||
|
free(type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
type->choice.sib4 = sib4;
|
||||||
|
add_sib_to_systeminformation(sysInfo, type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default :
|
default :
|
||||||
AssertFatal(false, "Invalid or not supported SIB%d\n", config_sibs[i]);
|
AssertFatal(false, "Invalid or not supported SIB%d\n", config_sibs[i]);
|
||||||
}
|
}
|
||||||
@@ -1019,7 +1048,7 @@ bool nr_mac_configure_other_sib(gNB_MAC_INST *nrmac, int num_cu_sib, const f1ap_
|
|||||||
int sib_idx = i + num_cu_sib;
|
int sib_idx = i + num_cu_sib;
|
||||||
config_sibs[sib_idx] = si->SIB_type;
|
config_sibs[sib_idx] = si->SIB_type;
|
||||||
switch (config_sibs[sib_idx]) {
|
switch (config_sibs[sib_idx]) {
|
||||||
case 19: {
|
case NR_SIB_19: {
|
||||||
struct NR_SystemInformation_IEs__sib_TypeAndInfo__Member *type_du = calloc(1, sizeof(*type_du));
|
struct NR_SystemInformation_IEs__sib_TypeAndInfo__Member *type_du = calloc(1, sizeof(*type_du));
|
||||||
type_du->present = NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib19_v1700;
|
type_du->present = NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib19_v1700;
|
||||||
NR_SIB19_r17_t *sib19 = get_SIB19_NR(cc->ServingCellConfigCommon);
|
NR_SIB19_r17_t *sib19 = get_SIB19_NR(cc->ServingCellConfigCommon);
|
||||||
|
|||||||
@@ -223,39 +223,67 @@ NR_UE_NR_Capability_t *decode_nr_ue_capability(int rnti, const NR_UE_CapabilityR
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
byte_array_t do_SIB2_NR(const NR_SIB2_t *sib2)
|
||||||
|
|
||||||
int do_SIB2_NR(uint8_t **msg_SIB2, NR_SSB_MTC_t *ssbmtc)
|
|
||||||
{
|
{
|
||||||
NR_SIB2_t *sib2 = calloc(1, sizeof(*sib2));
|
byte_array_t msg = {.buf = NULL, .len = 0};
|
||||||
sib2->cellReselectionInfoCommon.q_Hyst = NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB0;
|
char errbuf[256] = {0};
|
||||||
struct NR_SIB2__cellReselectionInfoCommon__speedStateReselectionPars *speed = calloc(1, sizeof(*speed));
|
size_t errlen = sizeof(errbuf);
|
||||||
NR_MobilityStateParameters_t mobilityStateParameters = {0};
|
int ret = asn_check_constraints(&asn_DEF_NR_SIB2, sib2, errbuf, &errlen);
|
||||||
mobilityStateParameters.t_Evaluation = NR_MobilityStateParameters__t_Evaluation_s30;
|
if (ret != 0) {
|
||||||
mobilityStateParameters.t_HystNormal = NR_MobilityStateParameters__t_HystNormal_s30;
|
LOG_E(NR_RRC, "SIB2 constraint check failed: %s\n", errbuf);
|
||||||
mobilityStateParameters.n_CellChangeMedium = 1;
|
return msg;
|
||||||
mobilityStateParameters.n_CellChangeHigh = 2;
|
}
|
||||||
speed->mobilityStateParameters = mobilityStateParameters;
|
|
||||||
struct NR_SIB2__cellReselectionInfoCommon__speedStateReselectionPars__q_HystSF qhyst = {0};
|
|
||||||
qhyst.sf_Medium = NR_SIB2__cellReselectionInfoCommon__speedStateReselectionPars__q_HystSF__sf_Medium_dB_4;
|
|
||||||
qhyst.sf_High = NR_SIB2__cellReselectionInfoCommon__speedStateReselectionPars__q_HystSF__sf_High_dB_6;
|
|
||||||
speed->q_HystSF = qhyst;
|
|
||||||
sib2->cellReselectionInfoCommon.speedStateReselectionPars = speed;
|
|
||||||
sib2->cellReselectionServingFreqInfo.cellReselectionPriority = 0; // INTEGER (0..7)
|
|
||||||
sib2->cellReselectionServingFreqInfo.threshServingLowP = 0;
|
|
||||||
NR_ReselectionThresholdQ_t *threshServingLowQ = calloc(1, sizeof(*threshServingLowQ));
|
|
||||||
*threshServingLowQ = 0;
|
|
||||||
sib2->cellReselectionServingFreqInfo.threshServingLowQ = threshServingLowQ;
|
|
||||||
sib2->intraFreqCellReselectionInfo.q_RxLevMin = -56; // INTEGER (-70..-22)
|
|
||||||
sib2->intraFreqCellReselectionInfo.s_IntraSearchP = 22; // INTEGER (0..31)
|
|
||||||
sib2->intraFreqCellReselectionInfo.t_ReselectionNR = 1; // INTEGER (0..7)
|
|
||||||
sib2->intraFreqCellReselectionInfo.deriveSSB_IndexFromCell = true;
|
|
||||||
sib2->intraFreqCellReselectionInfo.smtc = ssbmtc;
|
|
||||||
|
|
||||||
ssize_t size = uper_encode_to_new_buffer(&asn_DEF_NR_SIB2, NULL, (void *)sib2, (void **)msg_SIB2);
|
int val = uper_encode_to_new_buffer(&asn_DEF_NR_SIB2, NULL, (void *)sib2, (void **)&msg.buf);
|
||||||
AssertFatal (size > 0, "ASN1 message encoding failed (encoded %lu bytes)!\n", size);
|
if (val <= 0) {
|
||||||
ASN_STRUCT_FREE(asn_DEF_NR_SIB2, sib2);
|
LOG_E(NR_RRC, "Failed to encode SIB2\n");
|
||||||
return size;
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.len = val;
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte_array_t do_SIB3_NR(const NR_SIB3_t *sib3)
|
||||||
|
{
|
||||||
|
byte_array_t msg = {.buf = NULL, .len = 0};
|
||||||
|
char errbuf[256] = {0};
|
||||||
|
size_t errlen = sizeof(errbuf);
|
||||||
|
int ret = asn_check_constraints(&asn_DEF_NR_SIB3, sib3, errbuf, &errlen);
|
||||||
|
if (ret != 0) {
|
||||||
|
LOG_E(NR_RRC, "SIB3 constraint check failed: %s\n", errbuf);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
int val = uper_encode_to_new_buffer(&asn_DEF_NR_SIB3, NULL, (void *)sib3, (void **)&msg.buf);
|
||||||
|
if (val <= 0) {
|
||||||
|
LOG_E(NR_RRC, "Failed to encode SIB3\n");
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.len = val;
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte_array_t do_SIB4_NR(NR_SIB4_t *sib4)
|
||||||
|
{
|
||||||
|
byte_array_t msg = {.buf = NULL, .len = 0};
|
||||||
|
char errbuf[256] = {0};
|
||||||
|
size_t errlen = sizeof(errbuf);
|
||||||
|
int ret = asn_check_constraints(&asn_DEF_NR_SIB4, sib4, errbuf, &errlen);
|
||||||
|
if (ret != 0) {
|
||||||
|
LOG_E(NR_RRC, "SIB4 constraint check failed: %s\n", errbuf);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
int val = uper_encode_to_new_buffer(&asn_DEF_NR_SIB4, NULL, (void *)sib4, (void **)&msg.buf);
|
||||||
|
if (val <= 0) {
|
||||||
|
LOG_E(NR_RRC, "Failed to encode SIB4\n");
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.len = val;
|
||||||
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
int do_RRCReject(uint8_t *const buffer)
|
int do_RRCReject(uint8_t *const buffer)
|
||||||
|
|||||||
@@ -33,6 +33,9 @@
|
|||||||
#include "NR_MeasurementTimingConfiguration.h"
|
#include "NR_MeasurementTimingConfiguration.h"
|
||||||
#include "NR_UE-NR-Capability.h"
|
#include "NR_UE-NR-Capability.h"
|
||||||
#include "NR_UE-CapabilityRAT-ContainerList.h"
|
#include "NR_UE-CapabilityRAT-ContainerList.h"
|
||||||
|
#include "NR_SIB2.h"
|
||||||
|
#include "NR_SIB3.h"
|
||||||
|
#include "NR_SIB4.h"
|
||||||
#include "ds/seq_arr.h"
|
#include "ds/seq_arr.h"
|
||||||
#include "ds/byte_array.h"
|
#include "ds/byte_array.h"
|
||||||
#include "openair2/LAYER2/nr_pdcp/nr_pdcp_configuration.h"
|
#include "openair2/LAYER2/nr_pdcp/nr_pdcp_configuration.h"
|
||||||
@@ -65,7 +68,9 @@ typedef struct {
|
|||||||
*/
|
*/
|
||||||
int xer_sprint_NR(char *string, size_t string_size, struct asn_TYPE_descriptor_s *td, void *sptr);
|
int xer_sprint_NR(char *string, size_t string_size, struct asn_TYPE_descriptor_s *td, void *sptr);
|
||||||
|
|
||||||
int do_SIB2_NR(uint8_t **msg_SIB2, NR_SSB_MTC_t *ssbmtc);
|
byte_array_t do_SIB2_NR(const NR_SIB2_t *sib2);
|
||||||
|
byte_array_t do_SIB3_NR(const NR_SIB3_t *sib3);
|
||||||
|
byte_array_t do_SIB4_NR(NR_SIB4_t *sib4);
|
||||||
|
|
||||||
int do_RRCReject(uint8_t *const buffer);
|
int do_RRCReject(uint8_t *const buffer);
|
||||||
|
|
||||||
|
|||||||
@@ -14,12 +14,61 @@ extern "C" {
|
|||||||
#include "NR_DRB-ToAddMod.h"
|
#include "NR_DRB-ToAddMod.h"
|
||||||
#include "NR_DRB-ToAddModList.h"
|
#include "NR_DRB-ToAddModList.h"
|
||||||
#include "NR_SRB-ToAddModList.h"
|
#include "NR_SRB-ToAddModList.h"
|
||||||
|
#include "NR_InterFreqCarrierFreqInfo.h"
|
||||||
#include "ds/byte_array.h"
|
#include "ds/byte_array.h"
|
||||||
|
#include "NR_SIB2.h"
|
||||||
|
#include "NR_SIB3.h"
|
||||||
|
#include "NR_SIB4.h"
|
||||||
|
#include "NR_IntraFreqNeighCellInfo.h"
|
||||||
|
#include "NR_IntraFreqNeighCellList.h"
|
||||||
|
#include "NR_InterFreqCarrierFreqInfo.h"
|
||||||
|
#include "NR_InterFreqNeighCellInfo.h"
|
||||||
|
#include "NR_InterFreqNeighCellList.h"
|
||||||
|
#include "asn_application.h"
|
||||||
RAN_CONTEXT_t RC;
|
RAN_CONTEXT_t RC;
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static NR_SIB2_t *encode_and_decode_sib2(const NR_SIB2_t *sib2, byte_array_t *ba_out)
|
||||||
|
{
|
||||||
|
*ba_out = do_SIB2_NR(sib2);
|
||||||
|
EXPECT_GT(ba_out->len, 0);
|
||||||
|
EXPECT_NE(ba_out->buf, nullptr);
|
||||||
|
|
||||||
|
NR_SIB2_t *decoded = nullptr;
|
||||||
|
asn_dec_rval_t dec = uper_decode(nullptr, &asn_DEF_NR_SIB2, (void **)&decoded, ba_out->buf, ba_out->len, 0, 0);
|
||||||
|
EXPECT_EQ(dec.code, RC_OK);
|
||||||
|
EXPECT_NE(decoded, nullptr);
|
||||||
|
return decoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NR_SIB3_t *encode_and_decode_sib3(const NR_SIB3_t *sib3, byte_array_t *ba_out)
|
||||||
|
{
|
||||||
|
*ba_out = do_SIB3_NR(sib3);
|
||||||
|
EXPECT_GT(ba_out->len, 0);
|
||||||
|
EXPECT_NE(ba_out->buf, nullptr);
|
||||||
|
|
||||||
|
NR_SIB3_t *decoded = nullptr;
|
||||||
|
asn_dec_rval_t dec = uper_decode(nullptr, &asn_DEF_NR_SIB3, (void **)&decoded, ba_out->buf, ba_out->len, 0, 0);
|
||||||
|
EXPECT_EQ(dec.code, RC_OK);
|
||||||
|
EXPECT_NE(decoded, nullptr);
|
||||||
|
return decoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NR_SIB4_t *encode_and_decode_sib4(NR_SIB4_t *sib4, byte_array_t *ba_out)
|
||||||
|
{
|
||||||
|
*ba_out = do_SIB4_NR(sib4);
|
||||||
|
EXPECT_GT(ba_out->len, 0);
|
||||||
|
EXPECT_NE(ba_out->buf, nullptr);
|
||||||
|
|
||||||
|
NR_SIB4_t *decoded = nullptr;
|
||||||
|
asn_dec_rval_t dec = uper_decode(nullptr, &asn_DEF_NR_SIB4, (void **)&decoded, ba_out->buf, ba_out->len, 0, 0);
|
||||||
|
EXPECT_EQ(dec.code, RC_OK);
|
||||||
|
EXPECT_NE(decoded, nullptr);
|
||||||
|
return decoded;
|
||||||
|
}
|
||||||
|
|
||||||
TEST(nr_asn1, rrc_reject)
|
TEST(nr_asn1, rrc_reject)
|
||||||
{
|
{
|
||||||
unsigned char buf[1000];
|
unsigned char buf[1000];
|
||||||
@@ -138,6 +187,167 @@ TEST(nr_asn1, rrc_reconfiguration)
|
|||||||
free_RRCReconfiguration_params(params);
|
free_RRCReconfiguration_params(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(nr_asn1, sib2_basic_encode_decode)
|
||||||
|
{
|
||||||
|
NR_SIB2_t *sib2 = (NR_SIB2_t *)calloc_or_fail(1, sizeof(*sib2));
|
||||||
|
sib2->cellReselectionInfoCommon.q_Hyst = NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB0;
|
||||||
|
sib2->cellReselectionServingFreqInfo.cellReselectionPriority = 3;
|
||||||
|
sib2->cellReselectionServingFreqInfo.threshServingLowP = 10;
|
||||||
|
sib2->intraFreqCellReselectionInfo.q_RxLevMin = -56;
|
||||||
|
sib2->intraFreqCellReselectionInfo.s_IntraSearchP = 22;
|
||||||
|
sib2->intraFreqCellReselectionInfo.t_ReselectionNR = 1;
|
||||||
|
sib2->intraFreqCellReselectionInfo.deriveSSB_IndexFromCell = true;
|
||||||
|
|
||||||
|
byte_array_t ba = {};
|
||||||
|
NR_SIB2_t *decoded = encode_and_decode_sib2(sib2, &ba);
|
||||||
|
|
||||||
|
EXPECT_EQ(decoded->cellReselectionServingFreqInfo.cellReselectionPriority,
|
||||||
|
sib2->cellReselectionServingFreqInfo.cellReselectionPriority);
|
||||||
|
EXPECT_EQ(decoded->cellReselectionServingFreqInfo.threshServingLowP, sib2->cellReselectionServingFreqInfo.threshServingLowP);
|
||||||
|
|
||||||
|
ASN_STRUCT_FREE(asn_DEF_NR_SIB2, sib2);
|
||||||
|
ASN_STRUCT_FREE(asn_DEF_NR_SIB2, decoded);
|
||||||
|
free_byte_array(ba);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(nr_asn1, sib3_basic_encode_decode)
|
||||||
|
{
|
||||||
|
NR_SIB3_t *sib3 = (NR_SIB3_t *)calloc_or_fail(1, sizeof(*sib3));
|
||||||
|
sib3->intraFreqNeighCellList = (struct NR_IntraFreqNeighCellList *)calloc_or_fail(1, sizeof(*sib3->intraFreqNeighCellList));
|
||||||
|
|
||||||
|
NR_IntraFreqNeighCellInfo_t *cell = (NR_IntraFreqNeighCellInfo_t *)calloc_or_fail(1, sizeof(*cell));
|
||||||
|
cell->physCellId = 100;
|
||||||
|
cell->q_OffsetCell = NR_Q_OffsetRange_dB0;
|
||||||
|
ASN_SEQUENCE_ADD(&sib3->intraFreqNeighCellList->list, cell);
|
||||||
|
|
||||||
|
byte_array_t ba = {};
|
||||||
|
NR_SIB3_t *decoded = encode_and_decode_sib3(sib3, &ba);
|
||||||
|
|
||||||
|
ASSERT_NE(decoded->intraFreqNeighCellList, nullptr);
|
||||||
|
EXPECT_EQ(decoded->intraFreqNeighCellList->list.count, 1);
|
||||||
|
EXPECT_EQ(decoded->intraFreqNeighCellList->list.array[0]->physCellId, cell->physCellId);
|
||||||
|
|
||||||
|
ASN_STRUCT_FREE(asn_DEF_NR_SIB3, sib3);
|
||||||
|
ASN_STRUCT_FREE(asn_DEF_NR_SIB3, decoded);
|
||||||
|
free_byte_array(ba);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(nr_asn1, sib4_basic_encode_decode)
|
||||||
|
{
|
||||||
|
NR_SIB4_t *sib4 = (NR_SIB4_t *)calloc_or_fail(1, sizeof(*sib4));
|
||||||
|
|
||||||
|
NR_InterFreqCarrierFreqInfo_t *carrier = (NR_InterFreqCarrierFreqInfo_t *)calloc_or_fail(1, sizeof(*carrier));
|
||||||
|
carrier->dl_CarrierFreq = 2000;
|
||||||
|
carrier->ssbSubcarrierSpacing = NR_SubcarrierSpacing_kHz15;
|
||||||
|
carrier->deriveSSB_IndexFromCell = true;
|
||||||
|
carrier->q_RxLevMin = -56;
|
||||||
|
carrier->t_ReselectionNR = 1;
|
||||||
|
carrier->threshX_HighP = 5;
|
||||||
|
carrier->threshX_LowP = 3;
|
||||||
|
carrier->interFreqNeighCellList = (struct NR_InterFreqNeighCellList *)calloc_or_fail(1, sizeof(*carrier->interFreqNeighCellList));
|
||||||
|
|
||||||
|
NR_InterFreqNeighCellInfo_t *ncell = (NR_InterFreqNeighCellInfo_t *)calloc_or_fail(1, sizeof(*ncell));
|
||||||
|
ncell->physCellId = 200;
|
||||||
|
ncell->q_OffsetCell = NR_Q_OffsetRange_dB0;
|
||||||
|
ASN_SEQUENCE_ADD(&carrier->interFreqNeighCellList->list, ncell);
|
||||||
|
ASN_SEQUENCE_ADD(&sib4->interFreqCarrierFreqList.list, carrier);
|
||||||
|
|
||||||
|
byte_array_t ba = {};
|
||||||
|
NR_SIB4_t *decoded = encode_and_decode_sib4(sib4, &ba);
|
||||||
|
|
||||||
|
ASSERT_GT(decoded->interFreqCarrierFreqList.list.count, 0);
|
||||||
|
EXPECT_EQ(decoded->interFreqCarrierFreqList.list.array[0]->dl_CarrierFreq, carrier->dl_CarrierFreq);
|
||||||
|
|
||||||
|
ASN_STRUCT_FREE(asn_DEF_NR_SIB4, sib4);
|
||||||
|
ASN_STRUCT_FREE(asn_DEF_NR_SIB4, decoded);
|
||||||
|
free_byte_array(ba);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(nr_asn1, inter_freq_carrier_freq_info_ranges)
|
||||||
|
{
|
||||||
|
NR_SIB4_t *sib4 = (NR_SIB4_t *)calloc_or_fail(1, sizeof(*sib4));
|
||||||
|
ASN_SEQUENCE_ADD(&sib4->interFreqCarrierFreqList.list,
|
||||||
|
(NR_InterFreqCarrierFreqInfo_t *)calloc_or_fail(1, sizeof(NR_InterFreqCarrierFreqInfo_t)));
|
||||||
|
|
||||||
|
NR_InterFreqCarrierFreqInfo_t *carrier = sib4->interFreqCarrierFreqList.list.array[0];
|
||||||
|
carrier->dl_CarrierFreq = 640000;
|
||||||
|
carrier->ssbSubcarrierSpacing = NR_SubcarrierSpacing_kHz30;
|
||||||
|
carrier->deriveSSB_IndexFromCell = 1;
|
||||||
|
carrier->threshX_HighP = 0;
|
||||||
|
carrier->threshX_LowP = 0;
|
||||||
|
|
||||||
|
NR_Q_OffsetRange_t *qoff = (NR_Q_OffsetRange_t *)calloc_or_fail(1, sizeof(*qoff));
|
||||||
|
*qoff = 15; // arbitrary valid Q-OffsetRange enum
|
||||||
|
carrier->q_OffsetFreq = qoff;
|
||||||
|
|
||||||
|
// Optional threshX_Q struct
|
||||||
|
carrier->threshX_Q = (decltype(carrier->threshX_Q))calloc_or_fail(1, sizeof(*carrier->threshX_Q));
|
||||||
|
carrier->threshX_Q->threshX_HighQ = 0;
|
||||||
|
carrier->threshX_Q->threshX_LowQ = 0;
|
||||||
|
|
||||||
|
auto try_encode = [](NR_SIB4_t *s, bool expect_success) {
|
||||||
|
byte_array_t ba = do_SIB4_NR(s);
|
||||||
|
int len = (int)ba.len;
|
||||||
|
if (expect_success) {
|
||||||
|
EXPECT_GT(len, 0);
|
||||||
|
EXPECT_NE(ba.buf, nullptr);
|
||||||
|
} else {
|
||||||
|
EXPECT_LE(len, 0);
|
||||||
|
}
|
||||||
|
free_byte_array(ba);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Valid lower bounds for all constrained scalars
|
||||||
|
carrier->q_RxLevMin = -70;
|
||||||
|
carrier->t_ReselectionNR = 0;
|
||||||
|
carrier->threshX_HighP = 0;
|
||||||
|
carrier->threshX_LowP = 0;
|
||||||
|
carrier->threshX_Q->threshX_HighQ = 0;
|
||||||
|
carrier->threshX_Q->threshX_LowQ = 0;
|
||||||
|
*qoff = 15;
|
||||||
|
try_encode(sib4, true);
|
||||||
|
|
||||||
|
// Invalid q_RxLevMin (below ASN.1 range)
|
||||||
|
carrier->q_RxLevMin = -80;
|
||||||
|
carrier->t_ReselectionNR = 0;
|
||||||
|
try_encode(sib4, false);
|
||||||
|
|
||||||
|
// Invalid t_ReselectionNR (above ASN.1 range)
|
||||||
|
carrier->q_RxLevMin = -70;
|
||||||
|
carrier->t_ReselectionNR = 8;
|
||||||
|
try_encode(sib4, false);
|
||||||
|
|
||||||
|
// Restore valid q_RxLevMin / t_ReselectionNR
|
||||||
|
carrier->q_RxLevMin = -70;
|
||||||
|
carrier->t_ReselectionNR = 0;
|
||||||
|
|
||||||
|
// Invalid threshX_HighP (below 0)
|
||||||
|
carrier->threshX_HighP = -1;
|
||||||
|
try_encode(sib4, false);
|
||||||
|
carrier->threshX_HighP = 0;
|
||||||
|
|
||||||
|
// Invalid threshX_LowP (below 0)
|
||||||
|
carrier->threshX_LowP = -1;
|
||||||
|
try_encode(sib4, false);
|
||||||
|
carrier->threshX_LowP = 0;
|
||||||
|
|
||||||
|
// Invalid threshX_HighQ (below 0)
|
||||||
|
carrier->threshX_Q->threshX_HighQ = -1;
|
||||||
|
try_encode(sib4, false);
|
||||||
|
carrier->threshX_Q->threshX_HighQ = 0;
|
||||||
|
|
||||||
|
// Invalid threshX_LowQ (below 0)
|
||||||
|
carrier->threshX_Q->threshX_LowQ = -1;
|
||||||
|
try_encode(sib4, false);
|
||||||
|
carrier->threshX_Q->threshX_LowQ = 0;
|
||||||
|
|
||||||
|
// Invalid q_OffsetFreq (outside [-24,24] enum range)
|
||||||
|
*qoff = 99;
|
||||||
|
try_encode(sib4, false);
|
||||||
|
|
||||||
|
ASN_STRUCT_FREE(asn_DEF_NR_SIB4, sib4);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
logInit();
|
logInit();
|
||||||
|
|||||||
@@ -312,21 +312,107 @@ typedef struct {
|
|||||||
bool is_default_a3_configuration_exists;
|
bool is_default_a3_configuration_exists;
|
||||||
} nr_measurement_configuration_t;
|
} nr_measurement_configuration_t;
|
||||||
|
|
||||||
|
/** @brief Per-neighbor cell-specific offsets (TS 38.331), shared by SIB3 and SIB4
|
||||||
|
* Maps to ASN.1 IntraFreqNeighCellInfo and InterFreqNeighCellInfo
|
||||||
|
* (SIB3.IntraFreqNeighCellList, SIB4.InterFreqCarrierFreqInfo.neighCellList) */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
// q-OffsetCell: (Q-OffsetRange values -24,-22,-20,-18,-16,-14,-12,-10,-8,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,8,10,12,14,16,18,20,22,24 dB)
|
||||||
|
// per-neighbor cell ranking
|
||||||
|
int q_OffsetCell;
|
||||||
|
// q-RxLevMinOffsetCell: Q-OffsetCellSmall (1..8): Per-neighbor RSRP minimum offset
|
||||||
|
int q_RxLevMinOffsetCell;
|
||||||
|
// q-QualMinOffsetCell: Q-OffsetCellSmall (1..8): Per-neighbor RSRQ minimum offset
|
||||||
|
int q_QualMinOffsetCell;
|
||||||
|
} nr_neighbour_cell_neighbor_offset_t;
|
||||||
|
|
||||||
|
/** @brief SIB3 (intra-frequency) neighbor list parameters (TS 38.331)
|
||||||
|
* Per-neighbor fields for SIB3.IntraFreqNeighCellList.IntraFreqNeighCellInfo.
|
||||||
|
* SIB3 carries intra-freq neighbor offsets for ranking. */
|
||||||
|
typedef struct {
|
||||||
|
nr_neighbour_cell_neighbor_offset_t offset;
|
||||||
|
} nr_neighbour_cell_sib3_t;
|
||||||
|
|
||||||
|
/** @brief SIB4 inter-frequency carrier parameters (TS 38.331)
|
||||||
|
* Per-frequency fields for SIB4.InterFreqCarrierFreqList.InterFreqCarrierFreqInfo.
|
||||||
|
* Stored once per ARFCN; all neighbors on that frequency share this config. */
|
||||||
|
typedef struct {
|
||||||
|
// cellReselectionPriority (0..7): Absolute priority of this inter-freq carrier
|
||||||
|
int cellReselectionPriority;
|
||||||
|
// threshX-HighP (0..31): RSRP threshold for reselection to a higher-priority inter-freq
|
||||||
|
int threshX_HighP;
|
||||||
|
// threshX-LowP (0..31): RSRP threshold for reselection to a lower-priority inter-freq
|
||||||
|
int threshX_LowP;
|
||||||
|
// threshX-HighQ (0..31): RSRQ threshold for higher-priority inter-freq reselection
|
||||||
|
int threshX_HighQ;
|
||||||
|
// threshX-LowQ (0..31): RSRQ threshold for lower-priority inter-freq reselection
|
||||||
|
int threshX_LowQ;
|
||||||
|
// q-OffsetFreq (Q-OffsetRange values: -24,-22,-20,-18,-16,-14,-12,-10,-8,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,8,10,12,14,16,18,20,22,24 dB):
|
||||||
|
// Frequency-specific offset in inter-freq cell ranking formula
|
||||||
|
int q_OffsetFreq;
|
||||||
|
} nr_neighbour_cell_sib4_freq_t;
|
||||||
|
|
||||||
|
/** @brief SIB4 (inter-frequency) parameters per neighbor (TS 38.331)
|
||||||
|
* Combines per-frequency carrier info (InterFreqCarrierFreqInfo) and per-neighbor
|
||||||
|
* offsets (InterFreqNeighCellInfo). */
|
||||||
|
typedef struct {
|
||||||
|
// Per-frequency carrier (InterFreqCarrierFreqInfo)
|
||||||
|
nr_neighbour_cell_sib4_freq_t sib4_freq;
|
||||||
|
// Per-neighbor offsets (InterFreqNeighCellInfo)
|
||||||
|
nr_neighbour_cell_neighbor_offset_t offset;
|
||||||
|
} nr_neighbour_cell_sib4_t;
|
||||||
|
|
||||||
|
/** @brief Per-frequency SIB4 configuration (TS 38.331), keyed by ARFCN
|
||||||
|
* One entry per inter-freq carrier (absoluteFrequencySSB + subcarrierSpacing).
|
||||||
|
* freq_cfg holds InterFreqCarrierFreqInfo parameters. Analogous to one “carrier” slice of SIB4
|
||||||
|
* interFreqCarrierFreqList. */
|
||||||
|
typedef struct {
|
||||||
|
// absoluteFrequencySSB (ARFCN)
|
||||||
|
int arfcn;
|
||||||
|
// ssbSubcarrierSpacing
|
||||||
|
int scs;
|
||||||
|
// q-RxLevMin (-70..-22 dBm)
|
||||||
|
int q_RxLevMin;
|
||||||
|
// t-ReselectionNR (0..7)
|
||||||
|
int t_ReselectionNR;
|
||||||
|
// Inter-frequency carrier parameters (InterFreqCarrierFreqInfo)
|
||||||
|
nr_neighbour_cell_sib4_freq_t freq_cfg;
|
||||||
|
} nr_inter_freq_cfg_t;
|
||||||
|
|
||||||
|
/** @brief Neighbor cell configuration structure
|
||||||
|
* Single source of truth for neighbor cell information, used across multiple protocols/scopes:
|
||||||
|
* - Handover (NGAP/XnAP): for target gNB (ID, plmn, tac, nrcell_id, physicalCellId)
|
||||||
|
* - Measurement (MeasConfig): physicalCellId, absoluteFrequencySSB, band
|
||||||
|
* - DU validation: all fields validated against actual cell information
|
||||||
|
* - SIB3/SIB4 generation: physicalCellId, absoluteFrequencySSB, subcarrierSpacing, band
|
||||||
|
* References:
|
||||||
|
* - 3GPP TS 38.331 (RRC) for SIB3/SIB4 and MeasConfig
|
||||||
|
* - 3GPP TS 38.413 (NGAP) / TS 38.423 (XnAP) for target cell identification */
|
||||||
|
typedef struct {
|
||||||
|
// gNB identifier (for target node in NGAP/XnAP handover)
|
||||||
uint32_t gNB_ID;
|
uint32_t gNB_ID;
|
||||||
|
// NR Cell Global Identifier (for NGAP/XnAP handover, DU validation)
|
||||||
uint64_t nrcell_id;
|
uint64_t nrcell_id;
|
||||||
|
// Physical Cell ID (PCI) (used in HandoverPreparationInformation and MeasObjectNR)
|
||||||
int physicalCellId;
|
int physicalCellId;
|
||||||
|
// SSB absolute frequency (ARFCN) (for MeasObjectNR, intra/inter-frequency determination)
|
||||||
int absoluteFrequencySSB;
|
int absoluteFrequencySSB;
|
||||||
|
// SSB subcarrier spacing (for MeasObjectNR/SIB4)
|
||||||
int subcarrierSpacing;
|
int subcarrierSpacing;
|
||||||
|
// Frequency band indicator (for MeasObjectNR/SIB4)
|
||||||
int band;
|
int band;
|
||||||
|
// PLMN identity (for target node in NGAP/XnAP handover)
|
||||||
plmn_id_t plmn;
|
plmn_id_t plmn;
|
||||||
|
// Tracking Area Code (for target node in NGAP/XnAP handover)
|
||||||
uint32_t tac;
|
uint32_t tac;
|
||||||
bool isIntraFrequencyNeighbour;
|
// SIB3 (intra-frequency neighbor cell-specific offsets)
|
||||||
|
nr_neighbour_cell_sib3_t sib3;
|
||||||
|
// SIB4 (inter-frequency neighbor cell-specific parameters)
|
||||||
|
nr_neighbour_cell_sib4_t sib4;
|
||||||
} nr_neighbour_cell_t;
|
} nr_neighbour_cell_t;
|
||||||
|
|
||||||
typedef struct neighbour_cell_configuration_s {
|
typedef struct neighbour_cell_configuration_s {
|
||||||
uint64_t nr_cell_id;
|
uint64_t nr_cell_id;
|
||||||
seq_arr_t *neighbour_cells;
|
seq_arr_t neighbour_cells;
|
||||||
} neighbour_cell_configuration_t;
|
} neighbour_cell_configuration_t;
|
||||||
|
|
||||||
typedef struct nr_mac_rrc_dl_if_s {
|
typedef struct nr_mac_rrc_dl_if_s {
|
||||||
@@ -418,6 +504,101 @@ typedef struct nr_rrc_cuup_container_t {
|
|||||||
sctp_assoc_t assoc_id;
|
sctp_assoc_t assoc_id;
|
||||||
} nr_rrc_cuup_container_t;
|
} nr_rrc_cuup_container_t;
|
||||||
|
|
||||||
|
/**Timers for SIB2 MobilityStateParameters (TS 38.331)
|
||||||
|
* Value set shared by T-Evaluation and T-HystNormal: (s30, s60, s120, s180, s240). */
|
||||||
|
typedef enum sib2_mobility_state_timer_e {
|
||||||
|
SIB2_MOBILITY_STATE_TIMER_S30,
|
||||||
|
SIB2_MOBILITY_STATE_TIMER_S60,
|
||||||
|
SIB2_MOBILITY_STATE_TIMER_S120,
|
||||||
|
SIB2_MOBILITY_STATE_TIMER_S180,
|
||||||
|
SIB2_MOBILITY_STATE_TIMER_S240,
|
||||||
|
} sib2_mobility_state_timer_t;
|
||||||
|
|
||||||
|
/** Q-HystSF scaling factors for Q_hyst (TS 38.331)
|
||||||
|
* Speed-dependent offsets (-6, -4, -2, 0 dB) applied to the base Q_hyst
|
||||||
|
* when the UE is in medium or high mobility state. */
|
||||||
|
typedef enum sib2_q_hystsf_e {
|
||||||
|
SIB2_Q_HYSTSF_DB_6,
|
||||||
|
SIB2_Q_HYSTSF_DB_4,
|
||||||
|
SIB2_Q_HYSTSF_DB_2,
|
||||||
|
SIB2_Q_HYSTSF_DB0,
|
||||||
|
} sib2_q_hystsf_t;
|
||||||
|
|
||||||
|
/** SIB2 speedStateReselectionPars configuration
|
||||||
|
* Mirrors TS 38.331 MobilityStateParameters and Q-hystSF. */
|
||||||
|
typedef struct sib2_speed_state_reselection_pars_s {
|
||||||
|
/* t-Evaluation: duration for evaluating allowed reselections
|
||||||
|
* to enter mobility states (s30, s60, s120, s180, s240) */
|
||||||
|
sib2_mobility_state_timer_t t_Evaluation;
|
||||||
|
/* t-HystNormal: additional time period to evaluate reselection criteria
|
||||||
|
* before returning to normal mobility (s30, s60, s120, s180, s240) */
|
||||||
|
sib2_mobility_state_timer_t t_HystNormal;
|
||||||
|
/* n-CellChangeMedium: max number of cell reselections to enter medium mobility state (1..16) */
|
||||||
|
int n_CellChangeMedium;
|
||||||
|
/* n-CellChangeHigh: max number of cell reselections to enter high mobility state (1..16) */
|
||||||
|
int n_CellChangeHigh;
|
||||||
|
/* sf-Medium: speed-dependent scaling factor for Qhyst in medium state (-6, -4, -2, 0 dB) */
|
||||||
|
sib2_q_hystsf_t sf_Medium;
|
||||||
|
/* sf-High: speed-dependent scaling factor for Qhyst in high state (-6, -4, -2, 0 dB) */
|
||||||
|
sib2_q_hystsf_t sf_High;
|
||||||
|
} sib2_speed_state_reselection_pars_t;
|
||||||
|
|
||||||
|
/** SIB2 cellReselectionServingFreqInfo (TS 38.331) */
|
||||||
|
typedef struct {
|
||||||
|
/* s-NonIntraSearchP: Srxlev threshold to trigger NR inter-freq / inter-RAT measurements (0..31)*2 dB.
|
||||||
|
* Set to -1 to omit (optional ASN.1 field). */
|
||||||
|
int s_NonIntraSearchP;
|
||||||
|
/* s-NonIntraSearchQ: Squal threshold to trigger NR inter-freq / inter-RAT measurements (0..31)*2 dB */
|
||||||
|
int s_NonIntraSearchQ;
|
||||||
|
/* threshServingLowP: Srxlev threshold (dB) used by the UE on the serving cell
|
||||||
|
* when reselecting towards a lower-priority RAT/frequency (0..31)*2 dB */
|
||||||
|
int threshServingLowP;
|
||||||
|
/* threshServingLowQ: Squal threshold (dB) used by the UE on the serving cell
|
||||||
|
* when reselecting towards a lower-priority RAT/frequency (0..31)*2 dB.
|
||||||
|
* Set to -1 to omit (optional ASN.1 field). */
|
||||||
|
int threshServingLowQ;
|
||||||
|
/* cellReselectionPriority: absolute priority of serving NR frequency (0..7) */
|
||||||
|
int cellReselectionPriority;
|
||||||
|
} cell_reselection_serving_freq_info_t;
|
||||||
|
|
||||||
|
/** SIB2 cellReselectionInfoCommon (TS 38.331) */
|
||||||
|
typedef struct {
|
||||||
|
/* q-Hyst: hysteresis added to serving-cell ranking R_s (0..24 dB) */
|
||||||
|
int q_Hyst;
|
||||||
|
/* speedStateReselectionPars: MobilityStateParameters + q-HystSF */
|
||||||
|
sib2_speed_state_reselection_pars_t *speedStateReselectionPars;
|
||||||
|
} cell_reselection_info_common_t;
|
||||||
|
|
||||||
|
/** SIB2 intraFreqCellReselectionInfo (TS 38.331) */
|
||||||
|
typedef struct {
|
||||||
|
/* q-RxLevMin: minimum required RX level in the cell (dBm) */
|
||||||
|
int q_RxLevMin;
|
||||||
|
/* q-QualMin: minimum required quality level in the cell (dB); */
|
||||||
|
int q_QualMin;
|
||||||
|
/* s-IntraSearchP: Srxlev threshold for intra-freq meas (0..31) */
|
||||||
|
int s_IntraSearchP;
|
||||||
|
/* s-IntraSearchQ: Squal threshold for intra-freq meas (0..31) */
|
||||||
|
int s_IntraSearchQ;
|
||||||
|
/* t-ReselectionNR: NR cell reselection timer (0..7) */
|
||||||
|
int t_ReselectionNR;
|
||||||
|
} intra_freq_cell_reselection_info_t;
|
||||||
|
|
||||||
|
/** SIB2 configuration for idle/inactive cell reselection
|
||||||
|
* (maps to SIB2 reselection parameters in TS 38.304/38.331, per cell).
|
||||||
|
* @note Squal = Cell selection quality value (dB)
|
||||||
|
* Srxlev = Cell selection RX level value (dB) */
|
||||||
|
typedef struct sib2_config_s {
|
||||||
|
// cellReselectionInfoCommon
|
||||||
|
cell_reselection_info_common_t cell_reselection_info_common;
|
||||||
|
// cellReselectionServingFreqInfo
|
||||||
|
cell_reselection_serving_freq_info_t cell_reselection_serving_freq_info;
|
||||||
|
// intraFreqCellReselectionInfo
|
||||||
|
intra_freq_cell_reselection_info_t intra_freq_cell_reselection_info;
|
||||||
|
/* deriveSSB_IndexFromCell: SIB2 deriveSSB-IndexFromCell - whether UE may
|
||||||
|
* assume SFN/SSB alignment across cells on serving freq (per TS 38.304/38.133) */
|
||||||
|
bool deriveSSB_IndexFromCell;
|
||||||
|
} sib2_config_t;
|
||||||
|
|
||||||
//---NR---(completely change)---------------------
|
//---NR---(completely change)---------------------
|
||||||
typedef struct gNB_RRC_INST_s {
|
typedef struct gNB_RRC_INST_s {
|
||||||
|
|
||||||
@@ -432,6 +613,8 @@ typedef struct gNB_RRC_INST_s {
|
|||||||
// RRC configuration
|
// RRC configuration
|
||||||
nr_rrc_config_t configuration;
|
nr_rrc_config_t configuration;
|
||||||
seq_arr_t *SIBs;
|
seq_arr_t *SIBs;
|
||||||
|
// SIB2 configuration for cell reselection parameters
|
||||||
|
sib2_config_t sib2_config;
|
||||||
|
|
||||||
// gNB N3 GTPU instance
|
// gNB N3 GTPU instance
|
||||||
instance_t e1_inst;
|
instance_t e1_inst;
|
||||||
@@ -443,6 +626,9 @@ typedef struct gNB_RRC_INST_s {
|
|||||||
|
|
||||||
nr_mac_rrc_dl_if_t mac_rrc;
|
nr_mac_rrc_dl_if_t mac_rrc;
|
||||||
cucp_cuup_if_t cucp_cuup;
|
cucp_cuup_if_t cucp_cuup;
|
||||||
|
// Per-frequency SIB4 configurations, indexed by ARFCN
|
||||||
|
seq_arr_t inter_freqs; /* array of nr_inter_freq_cfg_t */
|
||||||
|
// Per-cell neighbour configurations, indexed by cell_id
|
||||||
seq_arr_t *neighbour_cell_configuration;
|
seq_arr_t *neighbour_cell_configuration;
|
||||||
nr_measurement_configuration_t measurementConfiguration;
|
nr_measurement_configuration_t measurementConfiguration;
|
||||||
|
|
||||||
|
|||||||
+13
-14
@@ -263,10 +263,8 @@ static bool eq_pci(const void *vval, const void *vit)
|
|||||||
|
|
||||||
const nr_neighbour_cell_t *get_neighbour_cell_by_pci(const neighbour_cell_configuration_t *cell, int pci)
|
const nr_neighbour_cell_t *get_neighbour_cell_by_pci(const neighbour_cell_configuration_t *cell, int pci)
|
||||||
{
|
{
|
||||||
seq_arr_t *head = cell->neighbour_cells;
|
DevAssert(cell);
|
||||||
DevAssert(head != NULL);
|
elm_arr_t e = find_if((seq_arr_t *)&cell->neighbour_cells, &pci, eq_pci);
|
||||||
LOG_D(NR_RRC, "Number of neighbour cells: %ld\n", head->size);
|
|
||||||
elm_arr_t e = find_if(head, &pci, eq_pci);
|
|
||||||
if (e.found) {
|
if (e.found) {
|
||||||
const nr_neighbour_cell_t *neighbour = (const nr_neighbour_cell_t *)e.it;
|
const nr_neighbour_cell_t *neighbour = (const nr_neighbour_cell_t *)e.it;
|
||||||
LOG_D(NR_RRC, "Found matching neighbour cell with PCI %d and Cell ID %ld\n", neighbour->physicalCellId, neighbour->nrcell_id);
|
LOG_D(NR_RRC, "Found matching neighbour cell with PCI %d and Cell ID %ld\n", neighbour->physicalCellId, neighbour->nrcell_id);
|
||||||
@@ -772,23 +770,23 @@ NR_MeasConfig_t *nr_rrc_get_measconfig(const gNB_RRC_INST *rrc, uint64_t nr_cell
|
|||||||
const NR_MeasTimingList_t *mtlist = cell->mtc->criticalExtensions.choice.c1->choice.measTimingConf->measTiming;
|
const NR_MeasTimingList_t *mtlist = cell->mtc->criticalExtensions.choice.c1->choice.measTimingConf->measTiming;
|
||||||
const NR_MeasTiming_t *mt = mtlist->list.array[0];
|
const NR_MeasTiming_t *mt = mtlist->list.array[0];
|
||||||
const neighbour_cell_configuration_t *neighbour_config = get_neighbour_cell_config(rrc, cell->info.cell_id);
|
const neighbour_cell_configuration_t *neighbour_config = get_neighbour_cell_config(rrc, cell->info.cell_id);
|
||||||
seq_arr_t *neighbour_cells = NULL;
|
const seq_arr_t *neighbour_cells = neighbour_config ? &neighbour_config->neighbour_cells : NULL;
|
||||||
if (neighbour_config)
|
|
||||||
neighbour_cells = neighbour_config->neighbour_cells;
|
|
||||||
|
|
||||||
int *neigh_a3_id = NULL;
|
int *neigh_a3_id = NULL;
|
||||||
if (neighbour_cells && neighbour_cells->size)
|
if (neighbour_cells && neighbour_cells->size)
|
||||||
neigh_a3_id = calloc_or_fail(neighbour_cells->size, sizeof(int));
|
neigh_a3_id = calloc_or_fail(neighbour_cells->size, sizeof(int));
|
||||||
|
|
||||||
if (neighbour_cells && rrc->measurementConfiguration.a3_event_list && rrc->measurementConfiguration.a3_event_list->size > 0) {
|
const nr_measurement_configuration_t *meas_cfg = &rrc->measurementConfiguration;
|
||||||
|
const seq_arr_t *a3_event_list = meas_cfg->a3_event_list;
|
||||||
|
if (neighbour_cells && a3_event_list && a3_event_list->size > 0) {
|
||||||
/* Loop through neighbours and find related A3 configuration
|
/* Loop through neighbours and find related A3 configuration
|
||||||
If no related A3 but there is default add the default one.
|
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.
|
If default one added once as a report, no need to add it again && duplication.
|
||||||
*/
|
*/
|
||||||
LOG_D(NR_RRC, "Preparing A3 Event Measurement Configuration!\n");
|
LOG_D(NR_RRC, "Preparing A3 Event Measurement Configuration!\n");
|
||||||
bool default_a3_added = false; // To ensure that the default configuration is only added once
|
bool default_a3_added = false; // To ensure that the default configuration is only added once
|
||||||
for (int i = 0; i < neighbour_cells->size; i++) {
|
int i = 0;
|
||||||
nr_neighbour_cell_t *neighbourCell = (nr_neighbour_cell_t *)seq_arr_at(neighbour_cells, i);
|
FOR_EACH_SEQ_ARR(nr_neighbour_cell_t *, neighbourCell, neighbour_cells) {
|
||||||
seq_arr_push_back(&neigh_seq, neighbourCell, sizeof(nr_neighbour_cell_t));
|
seq_arr_push_back(&neigh_seq, neighbourCell, sizeof(nr_neighbour_cell_t));
|
||||||
const nr_a3_event_t *a3Event = get_a3_configuration((gNB_RRC_INST *)rrc, neighbourCell->physicalCellId);
|
const nr_a3_event_t *a3Event = get_a3_configuration((gNB_RRC_INST *)rrc, neighbourCell->physicalCellId);
|
||||||
if (!a3Event) {
|
if (!a3Event) {
|
||||||
@@ -814,12 +812,13 @@ NR_MeasConfig_t *nr_rrc_get_measconfig(const gNB_RRC_INST *rrc, uint64_t nr_cell
|
|||||||
}
|
}
|
||||||
NR_ReportConfigId_t reportConfigId = neigh_a3_id[i];
|
NR_ReportConfigId_t reportConfigId = neigh_a3_id[i];
|
||||||
seq_arr_push_back(&rc_A3_seq, prepare_a3_event_report(a3Event, reportConfigId), sizeof(NR_ReportConfigToAddMod_t));
|
seq_arr_push_back(&rc_A3_seq, prepare_a3_event_report(a3Event, reportConfigId), sizeof(NR_ReportConfigToAddMod_t));
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rrc->measurementConfiguration.per_event)
|
if (meas_cfg->per_event)
|
||||||
rc_PER = prepare_periodic_event_report(rrc->measurementConfiguration.per_event);
|
rc_PER = prepare_periodic_event_report(meas_cfg->per_event);
|
||||||
if (rrc->measurementConfiguration.a2_event)
|
if (meas_cfg->a2_event)
|
||||||
rc_A2 = prepare_a2_event_report(rrc->measurementConfiguration.a2_event);
|
rc_A2 = prepare_a2_event_report(meas_cfg->a2_event);
|
||||||
|
|
||||||
NR_MeasConfig_t *result = get_MeasConfig(mt, band, cell->info.pci, rc_PER, rc_A2, &rc_A3_seq, &neigh_seq, neigh_a3_id);
|
NR_MeasConfig_t *result = get_MeasConfig(mt, band, cell->info.pci, rc_PER, rc_A2, &rc_A3_seq, &neigh_seq, neigh_a3_id);
|
||||||
|
|
||||||
|
|||||||
+450
-56
@@ -23,6 +23,11 @@
|
|||||||
#include "f1ap_messages_types.h"
|
#include "f1ap_messages_types.h"
|
||||||
#include "ngap_messages_types.h"
|
#include "ngap_messages_types.h"
|
||||||
#include "nr_rrc_defs.h"
|
#include "nr_rrc_defs.h"
|
||||||
|
#include "NR_SIB2.h"
|
||||||
|
#include "NR_SIB3.h"
|
||||||
|
#include "NR_SIB4.h"
|
||||||
|
#include "NR_Q-OffsetRange.h"
|
||||||
|
#include "openair2/RRC/NR/MESSAGES/asn1_msg.h"
|
||||||
#include "rrc_gNB_mobility.h"
|
#include "rrc_gNB_mobility.h"
|
||||||
#include "openair2/F1AP/f1ap_common.h"
|
#include "openair2/F1AP/f1ap_common.h"
|
||||||
#include "openair2/F1AP/f1ap_ids.h"
|
#include "openair2/F1AP/f1ap_ids.h"
|
||||||
@@ -33,6 +38,7 @@
|
|||||||
#include "s1ap_messages_types.h"
|
#include "s1ap_messages_types.h"
|
||||||
#include "tree.h"
|
#include "tree.h"
|
||||||
#include "uper_decoder.h"
|
#include "uper_decoder.h"
|
||||||
|
#include "common/utils/oai_asn1.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "xer_encoder.h"
|
#include "xer_encoder.h"
|
||||||
|
|
||||||
@@ -55,6 +61,375 @@ static NR_SSB_MTC_t *get_ssb_mtc(const NR_MeasurementTimingConfiguration_t *mtc)
|
|||||||
return ssb_mtc;
|
return ssb_mtc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief Map config q_Hyst (dB: 0,1,2,3,4,5,6,8,10..24) to ASN.1 q-Hyst enum index (0..15).
|
||||||
|
* ASN.1 NR_SIB2 cellReselectionInfoCommon.q-Hyst is encoded with 16 values. */
|
||||||
|
static long get_q_hyst_asn1(int q_hyst_db)
|
||||||
|
{
|
||||||
|
switch (q_hyst_db) {
|
||||||
|
case 0:
|
||||||
|
return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB0;
|
||||||
|
case 1:
|
||||||
|
return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB1;
|
||||||
|
case 2:
|
||||||
|
return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB2;
|
||||||
|
case 3:
|
||||||
|
return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB3;
|
||||||
|
case 4:
|
||||||
|
return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB4;
|
||||||
|
case 5:
|
||||||
|
return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB5;
|
||||||
|
case 6:
|
||||||
|
return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB6;
|
||||||
|
case 8:
|
||||||
|
return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB8;
|
||||||
|
case 10:
|
||||||
|
return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB10;
|
||||||
|
case 12:
|
||||||
|
return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB12;
|
||||||
|
case 14:
|
||||||
|
return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB14;
|
||||||
|
case 16:
|
||||||
|
return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB16;
|
||||||
|
case 18:
|
||||||
|
return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB18;
|
||||||
|
case 20:
|
||||||
|
return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB20;
|
||||||
|
case 22:
|
||||||
|
return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB22;
|
||||||
|
case 24:
|
||||||
|
return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB24;
|
||||||
|
default:
|
||||||
|
AssertFatal(false, "unexpected q-Hyst value %d (configuration should have been validated)\n", q_hyst_db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Build NR_SIB2 from validated SIB2 configuration.
|
||||||
|
* @param cfg Validated SIB2 configuration from config file
|
||||||
|
* @param ssbmtc SSB-MTC instance to attach to intraFreqCellReselectionInfo.smtc. */
|
||||||
|
static NR_SIB2_t *get_sib2_from_cfg(const sib2_config_t *cfg, NR_SSB_MTC_t *ssbmtc)
|
||||||
|
{
|
||||||
|
NR_SIB2_t *sib2 = calloc_or_fail(1, sizeof(*sib2));
|
||||||
|
typeof(sib2->cellReselectionInfoCommon) *common = &sib2->cellReselectionInfoCommon;
|
||||||
|
const cell_reselection_info_common_t *cfg_common = &cfg->cell_reselection_info_common;
|
||||||
|
|
||||||
|
/* q-Hyst: ASN.1 enum (0..15) converted from config dB (0,1,2,3,4,5,6,8,10..24). */
|
||||||
|
common->q_Hyst = get_q_hyst_asn1(cfg_common->q_Hyst);
|
||||||
|
|
||||||
|
/* speedStateReselectionPars */
|
||||||
|
if (cfg_common->speedStateReselectionPars != NULL) {
|
||||||
|
struct NR_SIB2__cellReselectionInfoCommon__speedStateReselectionPars *speed = calloc_or_fail(1, sizeof(*speed));
|
||||||
|
NR_MobilityStateParameters_t mobilityStateParameters = {0};
|
||||||
|
const sib2_speed_state_reselection_pars_t *pars = cfg_common->speedStateReselectionPars;
|
||||||
|
/* t-Evaluation / t-HystNormal: (0..7) */
|
||||||
|
mobilityStateParameters.t_Evaluation = pars->t_Evaluation;
|
||||||
|
mobilityStateParameters.t_HystNormal = pars->t_HystNormal;
|
||||||
|
/* n-CellChangeMedium / n-CellChangeHigh: (1..16) */
|
||||||
|
mobilityStateParameters.n_CellChangeMedium = pars->n_CellChangeMedium;
|
||||||
|
mobilityStateParameters.n_CellChangeHigh = pars->n_CellChangeHigh;
|
||||||
|
speed->mobilityStateParameters = mobilityStateParameters;
|
||||||
|
|
||||||
|
/* q-HystSF: sf-Medium/sf-High (0..3) */
|
||||||
|
struct NR_SIB2__cellReselectionInfoCommon__speedStateReselectionPars__q_HystSF qhyst = {0};
|
||||||
|
qhyst.sf_Medium = pars->sf_Medium;
|
||||||
|
qhyst.sf_High = pars->sf_High;
|
||||||
|
speed->q_HystSF = qhyst;
|
||||||
|
|
||||||
|
common->speedStateReselectionPars = speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* cellReselectionServingFreqInfo */
|
||||||
|
typeof(sib2->cellReselectionServingFreqInfo) *serv = &sib2->cellReselectionServingFreqInfo;
|
||||||
|
const cell_reselection_serving_freq_info_t *cfg_serv = &cfg->cell_reselection_serving_freq_info;
|
||||||
|
/* cellReselectionPriority: CellReselectionPriority (0..7) */
|
||||||
|
serv->cellReselectionPriority = cfg_serv->cellReselectionPriority;
|
||||||
|
/* threshServingLowP: ReselectionThreshold (0..31) */
|
||||||
|
serv->threshServingLowP = cfg_serv->threshServingLowP;
|
||||||
|
/* threshServingLowQ: ReselectionThresholdQ (0..31) */
|
||||||
|
if (cfg_serv->threshServingLowQ != -1)
|
||||||
|
asn1cCallocOne(serv->threshServingLowQ, cfg_serv->threshServingLowQ);
|
||||||
|
/* s-NonIntraSearchP/Q: ReselectionThreshold/ReselectionThresholdQ (0..31) */
|
||||||
|
if (cfg_serv->s_NonIntraSearchP != -1)
|
||||||
|
asn1cCallocOne(serv->s_NonIntraSearchP, cfg_serv->s_NonIntraSearchP);
|
||||||
|
if (cfg_serv->s_NonIntraSearchQ != -1)
|
||||||
|
asn1cCallocOne(serv->s_NonIntraSearchQ, cfg_serv->s_NonIntraSearchQ);
|
||||||
|
|
||||||
|
/* intraFreqCellReselectionInfo */
|
||||||
|
typeof(sib2->intraFreqCellReselectionInfo) *intra = &sib2->intraFreqCellReselectionInfo;
|
||||||
|
const intra_freq_cell_reselection_info_t *cfg_intra = &cfg->intra_freq_cell_reselection_info;
|
||||||
|
/* q-RxLevMin: Q-RxLevMin (-70..-22) */
|
||||||
|
intra->q_RxLevMin = cfg_intra->q_RxLevMin;
|
||||||
|
/* q-QualMin: Q-QualMin (-43..-12) */
|
||||||
|
if (cfg_intra->q_QualMin != -1)
|
||||||
|
asn1cCallocOne(intra->q_QualMin, cfg_intra->q_QualMin);
|
||||||
|
/* s-IntraSearchP: ReselectionThreshold (0..31) */
|
||||||
|
intra->s_IntraSearchP = cfg_intra->s_IntraSearchP;
|
||||||
|
/* s-IntraSearchQ: ReselectionThresholdQ (0..31) */
|
||||||
|
if (cfg_intra->s_IntraSearchQ != -1)
|
||||||
|
asn1cCallocOne(intra->s_IntraSearchQ, cfg_intra->s_IntraSearchQ);
|
||||||
|
/* t-ReselectionNR: (0..7) */
|
||||||
|
intra->t_ReselectionNR = cfg_intra->t_ReselectionNR;
|
||||||
|
/* deriveSSB-IndexFromCell (bool) */
|
||||||
|
intra->deriveSSB_IndexFromCell = cfg->deriveSSB_IndexFromCell;
|
||||||
|
/* smtc */
|
||||||
|
intra->smtc = ssbmtc;
|
||||||
|
|
||||||
|
return sib2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Map internal int enum to ASN.1 Q-OffsetRange value. */
|
||||||
|
static long get_q_offset_asn1(int q_offset_db)
|
||||||
|
{
|
||||||
|
switch (q_offset_db) {
|
||||||
|
case -24:
|
||||||
|
return NR_Q_OffsetRange_dB_24;
|
||||||
|
case -22:
|
||||||
|
return NR_Q_OffsetRange_dB_22;
|
||||||
|
case -20:
|
||||||
|
return NR_Q_OffsetRange_dB_20;
|
||||||
|
case -18:
|
||||||
|
return NR_Q_OffsetRange_dB_18;
|
||||||
|
case -16:
|
||||||
|
return NR_Q_OffsetRange_dB_16;
|
||||||
|
case -14:
|
||||||
|
return NR_Q_OffsetRange_dB_14;
|
||||||
|
case -12:
|
||||||
|
return NR_Q_OffsetRange_dB_12;
|
||||||
|
case -10:
|
||||||
|
return NR_Q_OffsetRange_dB_10;
|
||||||
|
case -8:
|
||||||
|
return NR_Q_OffsetRange_dB_8;
|
||||||
|
case -6:
|
||||||
|
return NR_Q_OffsetRange_dB_6;
|
||||||
|
case -5:
|
||||||
|
return NR_Q_OffsetRange_dB_5;
|
||||||
|
case -4:
|
||||||
|
return NR_Q_OffsetRange_dB_4;
|
||||||
|
case -3:
|
||||||
|
return NR_Q_OffsetRange_dB_3;
|
||||||
|
case -2:
|
||||||
|
return NR_Q_OffsetRange_dB_2;
|
||||||
|
case -1:
|
||||||
|
return NR_Q_OffsetRange_dB_1;
|
||||||
|
case 0:
|
||||||
|
return NR_Q_OffsetRange_dB0;
|
||||||
|
case 1:
|
||||||
|
return NR_Q_OffsetRange_dB1;
|
||||||
|
case 2:
|
||||||
|
return NR_Q_OffsetRange_dB2;
|
||||||
|
case 3:
|
||||||
|
return NR_Q_OffsetRange_dB3;
|
||||||
|
case 4:
|
||||||
|
return NR_Q_OffsetRange_dB4;
|
||||||
|
case 5:
|
||||||
|
return NR_Q_OffsetRange_dB5;
|
||||||
|
case 6:
|
||||||
|
return NR_Q_OffsetRange_dB6;
|
||||||
|
case 8:
|
||||||
|
return NR_Q_OffsetRange_dB8;
|
||||||
|
case 10:
|
||||||
|
return NR_Q_OffsetRange_dB10;
|
||||||
|
case 12:
|
||||||
|
return NR_Q_OffsetRange_dB12;
|
||||||
|
case 14:
|
||||||
|
return NR_Q_OffsetRange_dB14;
|
||||||
|
case 16:
|
||||||
|
return NR_Q_OffsetRange_dB16;
|
||||||
|
case 18:
|
||||||
|
return NR_Q_OffsetRange_dB18;
|
||||||
|
case 20:
|
||||||
|
return NR_Q_OffsetRange_dB20;
|
||||||
|
case 22:
|
||||||
|
return NR_Q_OffsetRange_dB22;
|
||||||
|
case 24:
|
||||||
|
return NR_Q_OffsetRange_dB24;
|
||||||
|
default:
|
||||||
|
AssertFatal(false, "Invalid q_offset_db value %d\n", q_offset_db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Build a SIB3 (intra-frequency neighbour cell list) from the configured neighbour cells.
|
||||||
|
* Only neighbours whose SSB ARFCN matches @param serving_ssb_arfcn are included.
|
||||||
|
* @param neighbours list of configured neighbour cells
|
||||||
|
* @param serving_ssb_arfcn SSB ARFCN of the serving cell, used to filter intra-frequency neighbours
|
||||||
|
* @returns allocated NR_SIB3_t on success, NULL if no matching neighbours were found */
|
||||||
|
static NR_SIB3_t *get_sib3_intra_freq_neighbors(const seq_arr_t *neighbours, uint32_t serving_ssb_arfcn)
|
||||||
|
{
|
||||||
|
DevAssert(neighbours);
|
||||||
|
|
||||||
|
NR_SIB3_t *sib3 = calloc_or_fail(1, sizeof(*sib3));
|
||||||
|
sib3->intraFreqNeighCellList = calloc_or_fail(1, sizeof(*sib3->intraFreqNeighCellList));
|
||||||
|
|
||||||
|
int intra_count = 0;
|
||||||
|
FOR_EACH_SEQ_ARR (const nr_neighbour_cell_t *, neigh, neighbours) {
|
||||||
|
if (neigh->absoluteFrequencySSB != serving_ssb_arfcn)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
LOG_D(NR_RRC,
|
||||||
|
"SIB3: intra-frequency neighbour candidate: cell ID %lu, PCI %d (SSB ARFCN %d)\n",
|
||||||
|
neigh->nrcell_id,
|
||||||
|
neigh->physicalCellId,
|
||||||
|
neigh->absoluteFrequencySSB);
|
||||||
|
|
||||||
|
NR_IntraFreqNeighCellInfo_t *cell = calloc_or_fail(1, sizeof(*cell));
|
||||||
|
cell->physCellId = neigh->physicalCellId;
|
||||||
|
|
||||||
|
const nr_neighbour_cell_neighbor_offset_t *off = &neigh->sib3.offset;
|
||||||
|
/* q-OffsetCell: NR_Q-OffsetRange (-24..24 dB), 0 dB default */
|
||||||
|
cell->q_OffsetCell = get_q_offset_asn1(off->q_OffsetCell);
|
||||||
|
|
||||||
|
/* q-RxLevMinOffsetCell / q-QualMinOffsetCell: (1..8) */
|
||||||
|
if (off->q_RxLevMinOffsetCell != -1)
|
||||||
|
asn1cCallocOne(cell->q_RxLevMinOffsetCell, off->q_RxLevMinOffsetCell);
|
||||||
|
if (off->q_QualMinOffsetCell != -1)
|
||||||
|
asn1cCallocOne(cell->q_QualMinOffsetCell, off->q_QualMinOffsetCell);
|
||||||
|
|
||||||
|
/* Add to SIB3 intra-frequency neighbour list */
|
||||||
|
if (sib3->intraFreqNeighCellList->list.count >= NR_maxCellIntra) {
|
||||||
|
LOG_W(NR_RRC, "SIB3: too many intra-frequency neighbors (max %d), skipping cell %ld\n", NR_maxCellIntra, neigh->nrcell_id);
|
||||||
|
asn1cFreeStruc(asn_DEF_NR_IntraFreqNeighCellInfo, cell);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
asn1cSeqAdd(&sib3->intraFreqNeighCellList->list, cell);
|
||||||
|
++intra_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intra_count == 0) {
|
||||||
|
LOG_W(NR_RRC,
|
||||||
|
"SIB3: no intra-frequency neighbours found for serving SSB ARFCN %u "
|
||||||
|
"(configured neighbours %u), not building SIB3\n",
|
||||||
|
serving_ssb_arfcn,
|
||||||
|
(unsigned)seq_arr_size(neighbours));
|
||||||
|
asn1cFreeStruc(asn_DEF_NR_SIB3, sib3);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sib3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Build an NR_InterFreqCarrierFreqInfo structure from a nr_inter_freq_cfg_t configuration.
|
||||||
|
* @param cfg Configuration to build the carrier from
|
||||||
|
* @return Allocated NR_InterFreqCarrierFreqInfo_t on success, NULL if the configuration is invalid */
|
||||||
|
static NR_InterFreqCarrierFreqInfo_t *build_inter_freq_carrier_from_cfg(const nr_inter_freq_cfg_t *cfg)
|
||||||
|
{
|
||||||
|
DevAssert(cfg);
|
||||||
|
|
||||||
|
const nr_neighbour_cell_sib4_freq_t *f = &cfg->freq_cfg;
|
||||||
|
|
||||||
|
NR_InterFreqCarrierFreqInfo_t *carrier = calloc_or_fail(1, sizeof(*carrier));
|
||||||
|
/* dl_CarrierFreq (ARFCN) */
|
||||||
|
carrier->dl_CarrierFreq = cfg->arfcn;
|
||||||
|
/* ssbSubcarrierSpacing: (15/30/60/120 kHz) */
|
||||||
|
carrier->ssbSubcarrierSpacing = cfg->scs;
|
||||||
|
carrier->deriveSSB_IndexFromCell = true;
|
||||||
|
/* Q-RxLevMin (-70..-22 dBm) */
|
||||||
|
carrier->q_RxLevMin = cfg->q_RxLevMin;
|
||||||
|
/* T-Reselection (0..7) */
|
||||||
|
carrier->t_ReselectionNR = cfg->t_ReselectionNR;
|
||||||
|
/* threshX_HighP/LowP: Reselection threshold (0..31) */
|
||||||
|
carrier->threshX_HighP = f->threshX_HighP;
|
||||||
|
carrier->threshX_LowP = f->threshX_LowP;
|
||||||
|
/* threshX-Q: Optional RSRQ thresholds; emit only when both are provided. */
|
||||||
|
if (f->threshX_HighQ != -1 && f->threshX_LowQ != -1) {
|
||||||
|
carrier->threshX_Q = calloc_or_fail(1, sizeof(*carrier->threshX_Q));
|
||||||
|
carrier->threshX_Q->threshX_HighQ = f->threshX_HighQ;
|
||||||
|
carrier->threshX_Q->threshX_LowQ = f->threshX_LowQ;
|
||||||
|
} else if (f->threshX_HighQ != -1 || f->threshX_LowQ != -1) {
|
||||||
|
LOG_W(NR_RRC,
|
||||||
|
"SIB4: partial threshX-Q for ARFCN %d (HighQ=%d LowQ=%d), omitting threshX-Q\n",
|
||||||
|
cfg->arfcn,
|
||||||
|
f->threshX_HighQ,
|
||||||
|
f->threshX_LowQ);
|
||||||
|
}
|
||||||
|
/* CellReselectionPriority (0..7) */
|
||||||
|
if (f->cellReselectionPriority != -1)
|
||||||
|
asn1cCallocOne(carrier->cellReselectionPriority, f->cellReselectionPriority);
|
||||||
|
/* q-OffsetFreq (-24..24 dB), 0 dB default */
|
||||||
|
asn1cCallocOne(carrier->q_OffsetFreq, get_q_offset_asn1(f->q_OffsetFreq));
|
||||||
|
/* InterFreqNeighCellList */
|
||||||
|
carrier->interFreqNeighCellList = calloc_or_fail(1, sizeof(*carrier->interFreqNeighCellList));
|
||||||
|
return carrier;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Build an NR_SIB4 structure populated with inter-frequency neighbour cell entries.
|
||||||
|
*
|
||||||
|
* Iterates over all configured inter-frequency carriers (excluding the serving cell ARFCN)
|
||||||
|
* and, for each carrier, adds the matching neighbour cells from @p neighbour_config.
|
||||||
|
* Carriers with no matching neighbours are discarded. If no valid carrier is found,
|
||||||
|
* the function returns NULL.
|
||||||
|
*
|
||||||
|
* @param neighbour_config Neighbour cell configuration (cells only).
|
||||||
|
* @param inter_freqs gNB-level inter-frequency configuration.
|
||||||
|
* @param serving_ssb_arfcn SSB ARFCN of the serving cell, used to skip the serving carrier.
|
||||||
|
* @return Allocated NR_SIB4_t on success, NULL if no inter-frequency neighbours are available.
|
||||||
|
*/
|
||||||
|
static NR_SIB4_t *get_sib4_inter_freq_neighbors(const neighbour_cell_configuration_t *neighbour_config,
|
||||||
|
const seq_arr_t *inter_freqs,
|
||||||
|
uint32_t serving_ssb_arfcn)
|
||||||
|
{
|
||||||
|
DevAssert(neighbour_config);
|
||||||
|
|
||||||
|
NR_SIB4_t *sib4 = calloc_or_fail(1, sizeof(*sib4));
|
||||||
|
|
||||||
|
const seq_arr_t *neigh = &neighbour_config->neighbour_cells;
|
||||||
|
|
||||||
|
FOR_EACH_SEQ_ARR (nr_inter_freq_cfg_t *, cfg, inter_freqs) {
|
||||||
|
if (cfg->arfcn == serving_ssb_arfcn)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
NR_InterFreqCarrierFreqInfo_t *carrier = build_inter_freq_carrier_from_cfg(cfg);
|
||||||
|
if (!carrier)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
NR_InterFreqNeighCellList_t *neigh_list = carrier->interFreqNeighCellList;
|
||||||
|
|
||||||
|
FOR_EACH_SEQ_ARR (nr_neighbour_cell_t *, nc, neigh) {
|
||||||
|
if (nc->absoluteFrequencySSB != cfg->arfcn || nc->subcarrierSpacing != cfg->scs)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (neigh_list->list.count >= NR_maxCellInter) {
|
||||||
|
LOG_W(NR_RRC,
|
||||||
|
"SIB4: too many inter-frequency neighbors for ARFCN %ld (max %d), skipping neighbour\n",
|
||||||
|
carrier->dl_CarrierFreq,
|
||||||
|
NR_maxCellInter);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nr_neighbour_cell_neighbor_offset_t *offset = &nc->sib4.offset;
|
||||||
|
LOG_I(NR_RRC,
|
||||||
|
"SIB4: added inter-frequency neighbour cell: cellId %ld (PCI %d), ARFCN %d, SCS %d\n",
|
||||||
|
nc->nrcell_id,
|
||||||
|
nc->physicalCellId,
|
||||||
|
cfg->arfcn,
|
||||||
|
cfg->scs);
|
||||||
|
asn1cSequenceAdd(neigh_list->list, NR_InterFreqNeighCellInfo_t, cell);
|
||||||
|
cell->physCellId = nc->physicalCellId;
|
||||||
|
|
||||||
|
/* q_OffsetCell: 0 dB is default */
|
||||||
|
cell->q_OffsetCell = get_q_offset_asn1(offset->q_OffsetCell);
|
||||||
|
|
||||||
|
/* q_RxLevMinOffsetCell / q_QualMinOffsetCell: range 1..8 */
|
||||||
|
if (offset->q_RxLevMinOffsetCell != -1)
|
||||||
|
asn1cCallocOne(cell->q_RxLevMinOffsetCell, offset->q_RxLevMinOffsetCell);
|
||||||
|
if (offset->q_QualMinOffsetCell != -1)
|
||||||
|
asn1cCallocOne(cell->q_QualMinOffsetCell, offset->q_QualMinOffsetCell);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (neigh_list->list.count > 0) {
|
||||||
|
asn1cSeqAdd(&sib4->interFreqCarrierFreqList.list, carrier);
|
||||||
|
} else {
|
||||||
|
ASN_STRUCT_FREE(asn_DEF_NR_InterFreqCarrierFreqInfo, carrier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sib4->interFreqCarrierFreqList.list.count == 0) {
|
||||||
|
ASN_STRUCT_FREE(asn_DEF_NR_SIB4, sib4);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sib4;
|
||||||
|
}
|
||||||
|
|
||||||
/** @brief Return the frequency of the SS block of the cell for which this message is included,
|
/** @brief Return the frequency of the SS block of the cell for which this message is included,
|
||||||
* or of other SS blocks within the same carrier. MeasurementTimingConfiguration message is
|
* or of other SS blocks within the same carrier. MeasurementTimingConfiguration message is
|
||||||
* used to convey assistance information for measurement timing between nodes (e.g F1 setup) */
|
* used to convey assistance information for measurement timing between nodes (e.g F1 setup) */
|
||||||
@@ -152,7 +527,7 @@ static int nr_cell_id_match(const void *key, const void *element)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static neighbour_cell_configuration_t *get_cell_neighbour_list(const gNB_RRC_INST *rrc, const nr_rrc_cell_container_t *cell)
|
static const neighbour_cell_configuration_t *get_cell_neighbour_list(const gNB_RRC_INST *rrc, const nr_rrc_cell_container_t *cell)
|
||||||
{
|
{
|
||||||
void *base = seq_arr_front(rrc->neighbour_cell_configuration);
|
void *base = seq_arr_front(rrc->neighbour_cell_configuration);
|
||||||
size_t nmemb = seq_arr_size(rrc->neighbour_cell_configuration);
|
size_t nmemb = seq_arr_size(rrc->neighbour_cell_configuration);
|
||||||
@@ -160,42 +535,7 @@ static neighbour_cell_configuration_t *get_cell_neighbour_list(const gNB_RRC_INS
|
|||||||
|
|
||||||
void *it = bsearch((void *)&cell->info.cell_id, base, nmemb, size, nr_cell_id_match);
|
void *it = bsearch((void *)&cell->info.cell_id, base, nmemb, size, nr_cell_id_match);
|
||||||
|
|
||||||
return (neighbour_cell_configuration_t *)it;
|
return (const neighbour_cell_configuration_t *)it;
|
||||||
}
|
|
||||||
|
|
||||||
static void is_intra_frequency_neighbour(void *ssb_arfcn, void *neighbour_cell)
|
|
||||||
{
|
|
||||||
uint32_t *ssb_arfcn_ptr = (uint32_t *)ssb_arfcn;
|
|
||||||
nr_neighbour_cell_t *neighbour_cell_ptr = (nr_neighbour_cell_t *)neighbour_cell;
|
|
||||||
|
|
||||||
if (*ssb_arfcn_ptr == neighbour_cell_ptr->absoluteFrequencySSB) {
|
|
||||||
LOG_D(NR_RRC, "HO LOG: found intra frequency neighbour %lu!\n", neighbour_cell_ptr->nrcell_id);
|
|
||||||
neighbour_cell_ptr->isIntraFrequencyNeighbour = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @brief Labels neighbour cells if they are intra frequency to prepare meas config only for intra frequency ho
|
|
||||||
* @param[in] rrc Pointer to RRC instance
|
|
||||||
* @param[in] cell Pointer to cell container
|
|
||||||
*/
|
|
||||||
static void label_intra_frequency_neighbours(gNB_RRC_INST *rrc, const nr_rrc_cell_container_t *cell)
|
|
||||||
{
|
|
||||||
if (!rrc->neighbour_cell_configuration)
|
|
||||||
return;
|
|
||||||
|
|
||||||
neighbour_cell_configuration_t *neighbour_cell_config = get_cell_neighbour_list(rrc, cell);
|
|
||||||
if (!neighbour_cell_config)
|
|
||||||
return;
|
|
||||||
|
|
||||||
uint32_t ssb_arfcn = get_ssb_arfcn(cell);
|
|
||||||
LOG_D(NR_RRC,
|
|
||||||
"Cell %lu (PCI %d, SSB ARFCN %u) has neighbour cell configuration, labeling intra-frequency neighbours\n",
|
|
||||||
cell->info.cell_id,
|
|
||||||
cell->info.pci,
|
|
||||||
ssb_arfcn);
|
|
||||||
|
|
||||||
seq_arr_t *cell_neighbour_list = neighbour_cell_config->neighbour_cells;
|
|
||||||
for_each(cell_neighbour_list, (void *)&ssb_arfcn, is_intra_frequency_neighbour);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool valid_du_in_neighbour_configs(const seq_arr_t *neighbour_cell_configuration, const f1ap_served_cell_info_t *cell)
|
static bool valid_du_in_neighbour_configs(const seq_arr_t *neighbour_cell_configuration, const f1ap_served_cell_info_t *cell)
|
||||||
@@ -206,10 +546,8 @@ static bool valid_du_in_neighbour_configs(const seq_arr_t *neighbour_cell_config
|
|||||||
int ssb_arfcn = ssb_arfcn_mtc(mtc);
|
int ssb_arfcn = ssb_arfcn_mtc(mtc);
|
||||||
ASN_STRUCT_FREE(asn_DEF_NR_MeasurementTimingConfiguration, mtc);
|
ASN_STRUCT_FREE(asn_DEF_NR_MeasurementTimingConfiguration, mtc);
|
||||||
|
|
||||||
for (int c = 0; c < neighbour_cell_configuration->size; c++) {
|
FOR_EACH_SEQ_ARR(const neighbour_cell_configuration_t *, neighbour_config, neighbour_cell_configuration) {
|
||||||
const neighbour_cell_configuration_t *neighbour_config = seq_arr_at(neighbour_cell_configuration, c);
|
FOR_EACH_SEQ_ARR(const nr_neighbour_cell_t *, nc, &neighbour_config->neighbour_cells) {
|
||||||
for (int ni = 0; ni < neighbour_config->neighbour_cells->size; ni++) {
|
|
||||||
const nr_neighbour_cell_t *nc = seq_arr_at(neighbour_config->neighbour_cells, ni);
|
|
||||||
if (nc->nrcell_id != cell->nr_cellid)
|
if (nc->nrcell_id != cell->nr_cellid)
|
||||||
continue;
|
continue;
|
||||||
// current cell is in the nc config, check that config matches
|
// current cell is in the nc config, check that config matches
|
||||||
@@ -299,6 +637,17 @@ static void cp_f1_served_cell_info_to_cell(nr_rrc_cell_container_t *dst, const f
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void add_si_msg(served_cells_to_activate_t *cell, int sib_type, const byte_array_t *enc)
|
||||||
|
{
|
||||||
|
AssertFatal(cell->num_SI < F1AP_MAX_NO_SIB_TYPES,
|
||||||
|
"Too many SI messages (%u), max supported is %u\n",
|
||||||
|
cell->num_SI,
|
||||||
|
F1AP_MAX_NO_SIB_TYPES);
|
||||||
|
cell->SI_msg[cell->num_SI].SI_container = copy_byte_array(*enc);
|
||||||
|
cell->SI_msg[cell->num_SI].SI_type = sib_type;
|
||||||
|
cell->num_SI++;
|
||||||
|
}
|
||||||
|
|
||||||
void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req, sctp_assoc_t assoc_id)
|
void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req, sctp_assoc_t assoc_id)
|
||||||
{
|
{
|
||||||
AssertFatal(assoc_id != 0, "illegal assoc_id == 0: should be -1 (monolithic) or >0 (split)\n");
|
AssertFatal(assoc_id != 0, "illegal assoc_id == 0: should be -1 (monolithic) or >0 (split)\n");
|
||||||
@@ -427,16 +776,68 @@ void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req, sctp_assoc_t assoc_id)
|
|||||||
// Encode CU SIBs and configure setup response with sysinfo
|
// Encode CU SIBs and configure setup response with sysinfo
|
||||||
seq_arr_t *sibs = rrc->SIBs;
|
seq_arr_t *sibs = rrc->SIBs;
|
||||||
if (sibs) {
|
if (sibs) {
|
||||||
for (int i = 0; i < sibs->size; i++) {
|
FOR_EACH_SEQ_ARR (nr_SIBs_t *, sib, sibs) {
|
||||||
nr_SIBs_t *sib = (nr_SIBs_t *)seq_arr_at(sibs, i);
|
|
||||||
switch (sib->SIB_type) {
|
switch (sib->SIB_type) {
|
||||||
case 2: {
|
case NR_SIB_2: {
|
||||||
NR_SSB_MTC_t *ssbmtc = get_ssb_mtc(new->mtc);
|
NR_SSB_MTC_t *ssbmtc = get_ssb_mtc(new->mtc);
|
||||||
sib->SIB_size = do_SIB2_NR(&sib->SIB_buffer, ssbmtc);
|
NR_SIB2_t *sib2 = get_sib2_from_cfg(&rrc->sib2_config, ssbmtc);
|
||||||
cell.SI_msg[cell.num_SI].SI_container = sib->SIB_buffer;
|
byte_array_t enc = do_SIB2_NR(sib2);
|
||||||
cell.SI_msg[cell.num_SI].SI_container_length = sib->SIB_size;
|
ASN_STRUCT_FREE(asn_DEF_NR_SIB2, sib2);
|
||||||
cell.SI_msg[cell.num_SI].SI_type = sib->SIB_type;
|
if (!enc.buf || enc.len == 0) {
|
||||||
cell.num_SI++;
|
free_byte_array(enc);
|
||||||
|
LOG_E(NR_RRC, "SIB2 encoding failed\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
add_si_msg(&cell, sib->SIB_type, &enc);
|
||||||
|
free_byte_array(enc);
|
||||||
|
LOG_I(NR_RRC, "DU %ld: added SIB2 to F1 Setup Response (cell %ld)\n", du->gNB_DU_id, new->info.cell_id);
|
||||||
|
} break;
|
||||||
|
case NR_SIB_3: {
|
||||||
|
if (!rrc->neighbour_cell_configuration)
|
||||||
|
break;
|
||||||
|
const neighbour_cell_configuration_t *neighbour_config = get_cell_neighbour_list(rrc, new);
|
||||||
|
if (!neighbour_config)
|
||||||
|
break;
|
||||||
|
const seq_arr_t *neigh_cells = &neighbour_config->neighbour_cells;
|
||||||
|
|
||||||
|
NR_SIB3_t *sib3 = get_sib3_intra_freq_neighbors(neigh_cells, get_ssb_arfcn(new));
|
||||||
|
if (!sib3)
|
||||||
|
break;
|
||||||
|
|
||||||
|
byte_array_t enc = do_SIB3_NR(sib3);
|
||||||
|
ASN_STRUCT_FREE(asn_DEF_NR_SIB3, sib3);
|
||||||
|
if (!enc.buf || enc.len == 0) {
|
||||||
|
free_byte_array(enc);
|
||||||
|
LOG_E(NR_RRC, "SIB3 encoding failed\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
add_si_msg(&cell, sib->SIB_type, &enc);
|
||||||
|
free_byte_array(enc);
|
||||||
|
LOG_I(NR_RRC, "DU %ld: added SIB3 to F1 Setup Response (cell %ld)\n", du->gNB_DU_id, new->info.cell_id);
|
||||||
|
} break;
|
||||||
|
case NR_SIB_4: {
|
||||||
|
if (!rrc->neighbour_cell_configuration)
|
||||||
|
break;
|
||||||
|
const neighbour_cell_configuration_t *neighbour_cfg = get_cell_neighbour_list(rrc, new);
|
||||||
|
if (!neighbour_cfg)
|
||||||
|
break;
|
||||||
|
|
||||||
|
NR_SIB4_t *sib4 = get_sib4_inter_freq_neighbors(neighbour_cfg, &rrc->inter_freqs, get_ssb_arfcn(new));
|
||||||
|
if (!sib4) {
|
||||||
|
LOG_W(NR_RRC, "SIB4: could not build from neighbours, skipping\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte_array_t enc = do_SIB4_NR(sib4);
|
||||||
|
ASN_STRUCT_FREE(asn_DEF_NR_SIB4, sib4);
|
||||||
|
if (!enc.buf || enc.len == 0) {
|
||||||
|
free_byte_array(enc);
|
||||||
|
LOG_E(NR_RRC, "SIB4 encoding failed\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
add_si_msg(&cell, sib->SIB_type, &enc);
|
||||||
|
free_byte_array(enc);
|
||||||
|
LOG_I(NR_RRC, "DU %ld: added SIB4 to F1 Setup Response (cell %ld)\n", du->gNB_DU_id, new->info.cell_id);
|
||||||
} break;
|
} break;
|
||||||
default:
|
default:
|
||||||
AssertFatal(false, "SIB%d not handled yet\n", sib->SIB_type);
|
AssertFatal(false, "SIB%d not handled yet\n", sib->SIB_type);
|
||||||
@@ -444,9 +845,6 @@ void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req, sctp_assoc_t assoc_id)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new->mib != NULL &&new->sib1 != NULL)
|
|
||||||
label_intra_frequency_neighbours(rrc, new);
|
|
||||||
|
|
||||||
resp.cells_to_activate[i] = cell;
|
resp.cells_to_activate[i] = cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -662,10 +1060,6 @@ void rrc_gNB_process_f1_du_configuration_update(f1ap_gnb_du_configuration_update
|
|||||||
LOG_I(NR_RRC, "update system information of DU %ld\n", du->gNB_DU_id);
|
LOG_I(NR_RRC, "update system information of DU %ld\n", du->gNB_DU_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cell && cell->mib != NULL && cell->sib1 != NULL && cell->assoc_id == du->assoc_id) {
|
|
||||||
label_intra_frequency_neighbours(rrc, cell);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conf_up->num_cells_to_delete > 0) {
|
if (conf_up->num_cells_to_delete > 0) {
|
||||||
|
|||||||
@@ -607,7 +607,6 @@ void nr_HO_N2_trigger_telnet(gNB_RRC_INST *rrc, uint32_t neighbour_pci, uint32_t
|
|||||||
if (neighbour_pci == scell_pci) {
|
if (neighbour_pci == scell_pci) {
|
||||||
LOG_I(NR_RRC, "UE %d: trigger handover on the same cell PCI=%d\n", rrc_ue_id, neighbour_pci);
|
LOG_I(NR_RRC, "UE %d: trigger handover on the same cell PCI=%d\n", rrc_ue_id, neighbour_pci);
|
||||||
nr_neighbour_cell_t neighbourConfig = {
|
nr_neighbour_cell_t neighbourConfig = {
|
||||||
.isIntraFrequencyNeighbour = true,
|
|
||||||
.gNB_ID = du->gNB_DU_id,
|
.gNB_ID = du->gNB_DU_id,
|
||||||
.nrcell_id = pcell->info.cell_id,
|
.nrcell_id = pcell->info.cell_id,
|
||||||
.physicalCellId = pcell->info.pci,
|
.physicalCellId = pcell->info.pci,
|
||||||
|
|||||||
Reference in New Issue
Block a user