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:
Robert Schmidt
2026-04-16 12:42:42 +02:00
25 changed files with 1857 additions and 279 deletions
@@ -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;
} }
); );
} }
+2 -2
View File
@@ -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;
+53 -9
View File
@@ -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;
} }
+2 -1
View File
@@ -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);
+26 -3
View File
@@ -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
View File
@@ -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
+92
View File
@@ -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
View File
@@ -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;
} }
); );
} }
+10 -2
View File
@@ -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.
+1 -2
View File
@@ -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;
+15 -24
View File
@@ -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);
+9 -9
View File
@@ -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
View File
@@ -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))
+14 -5
View File
@@ -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;
} }
+2
View File
@@ -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
View File
@@ -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"
+39 -10
View File
@@ -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);
+59 -31
View File
@@ -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)
+6 -1
View File
@@ -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();
+188 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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) {
-1
View File
@@ -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,