13 Commits
Author SHA1 Message Date
maki 02dc8dadd4 update 2025-12-13 14:51:30 +09:00
maki 63d2238429 laptop init 2025-12-07 13:47:47 +09:00
maki 524e26ede5 Merge branch 'develop' of https://git.aubsebian.net/maki/openairinterface5g into develop 2025-12-06 18:16:12 +09:00
maki 01db9857c4 dec3 2025-12-03 13:51:53 +09:00
maki aa353fdcff dec3 2025-12-03 13:48:14 +09:00
maki bdfe516aa7 update 2025-12-01 19:12:08 +09:00
maki f8f10665e7 update 2025-12-01 14:49:02 +09:00
maki ee3e027041 update 2025-11-20 21:00:28 +09:00
maki 7aca2a5fdf useful 2025-11-18 19:09:53 +09:00
maki 636c124596 update 2025-11-18 18:59:13 +09:00
maki 7a18e84f1b update 2025-11-18 18:36:26 +09:00
maki b30f738b01 Merge tag '2025.w42' into HEAD
Integration 2025 week 42

* !3623 Optimization of PDSCH precoding operation
* !3691 \[CI\] Ensure multi-arch DockerHub Push after successful image build
* !3696 asn1c doesn't follow c99 variables alias rules, it needs to be compiled this...
* !3685 Assign antenna ports correctly for 1 and 2 layers for aerial.
* !3495 Type0 PDSCH frequency allocation at L1 UE
* !3686 Remove NR L2 simulator code from UE
* !3693 Remove B200-specific clock-rate setting
* !3701 OAI UE: add shared RSRP Table 10.1.6.1-1 mapping function to helpers and adopt it RRC UE
* !3662 RRC UE file cleanup
* update doc/README.md
* !3608 use common scale channel functions for DLSCH and ULSCH
* !3694 fix non initialized DMRS port value in case of DCI00 at UE
* !3702 Extend F1 rfsim CI test
* !3704 remove unused global queues, dead code
* !3673 CI: Aerial: Add an UL-heavy pipeline test
* !3651 CI: Upgrade HO setup to use an attenuator
* !3695 CI: Upgrade Aerial setup to 25-2.1
2025-10-20 16:11:42 +09:00
maki ac4dbff76e nakaolab oct19 2025-10-19 20:03:22 +09:00
49 changed files with 8681 additions and 153 deletions
+1
View File
@@ -5,3 +5,4 @@ common/utils/T/T_IDs.h
common/utils/T/T_messages.txt.h common/utils/T/T_messages.txt.h
common/utils/T/genids common/utils/T/genids
common/utils/T/genids.o common/utils/T/genids.o
artifacts
+15
View File
@@ -0,0 +1,15 @@
#!/bin/bash
if [[ $(/usr/bin/id -u) -ne 0 ]]; then
echo "Not running as root"
exit
fi
cd openair2/E2AP/flexric/
rm -rf build && mkdir build && \
cd build && cmake -DE2AP_VERSION=E2AP_V2 -DKPM_VERSION=KPM_V2_03 .. && make -j16 && sudo make install
cd ~/OAI/openairinterface5g/
cp -r /usr/local/etc/flexric/ flexric/etc/
cp -r /usr/local/lib/flexric/ flexric/lib/
+19 -10
View File
@@ -9,20 +9,29 @@ channelmod = {
model_name = "rfsimu_channel_enB0" model_name = "rfsimu_channel_enB0"
type = "AWGN"; type = "AWGN";
ploss_dB = 0; ploss_dB = 0;
noise_power_dB = -10; noise_power_dB = -60;
forgetfact = 0; # forgetfact = 0;
offset = 0; # offset = 0;
ds_tdl = 0; # ds_tdl = 0;
}, },
{ {
model_name = "rfsimu_channel_ue0" model_name = "rfsimu_channel_ue0"
type = "AWGN"; type = "AWGN";
ploss_dB = 0; # ploss_dB = 0;
noise_power_dB = 0; # noise_power_dB = -30;
forgetfact = 0; # forgetfact = 0;
offset = 0; # offset = 0;
ds_tdl = 0; # ds_tdl = 0;
} },
{
model_name = "rfsimu_channel_ue2"
type = "AWGN";
# ploss_dB = 0;
# noise_power_dB = 0;
# forgetfact = 0;
# offset = 0;
# ds_tdl = 0;
}
); );
modellist_rfsimu_2 = ( modellist_rfsimu_2 = (
{ {
@@ -40,7 +40,6 @@ gNBs =
NETWORK_INTERFACES : NETWORK_INTERFACES :
{ {
GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.71.150"; GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.71.150";
GNB_IPV4_ADDRESS_FOR_NGU = "192.168.71.150"; GNB_IPV4_ADDRESS_FOR_NGU = "192.168.71.150";
GNB_PORT_FOR_S1U = 2152; # Spec 2152 GNB_PORT_FOR_S1U = 2152; # Spec 2152
@@ -0,0 +1,332 @@
Active_gNBs = ("gnb-rfsim");
# Asn1_verbosity, choice in: none, info, annoying
Asn1_verbosity = "none";
e2_agent = {
enabled = "yes";
near_ric_ip_addr = "192.168.71.21";
near_ric_port = 36421;
sm_dir = "/usr/local/lib/flexric/";
}
gNBs =
(
{
////////// Identification parameters:
gNB_ID = 0xe00;
gNB_name = "gnb-rfsim";
// Tracking area code, 0x0000 and 0xfffe are reserved values
tracking_area_code = 1;
# plmn_list = ({ mcc = 001; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1; sd = 0x1; }) });
plmn_list = ({ mcc = 208; mnc = 99; mnc_length = 2; snssaiList = ({ sst = 1; sd = 0xffffff; }) });
nr_cellid = 12345678L;
////////// Physical parameters:
pdsch_AntennaPorts_XP = 2;
pusch_AntennaPorts = 2;
do_CSIRS = 1;
do_SRS = 1;
min_rxtxtime = 6;
pdcch_ConfigSIB1 = (
{
controlResourceSetZero = 12;
searchSpaceZero = 0;
}
);
servingCellConfigCommon = (
{
#spCellConfigCommon
physCellId = 0;
# downlinkConfigCommon
#frequencyInfoDL
# this is 3300.60 MHz + 53*12*30e-3 MHz = 3319.68
absoluteFrequencySSB = 621312; # 641280
dl_frequencyBand = 78;
# this is 3300.60 MHz
dl_absoluteFrequencyPointA = 620040; # 640008
#scs-SpecificCarrierList
dl_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
dl_subcarrierSpacing = 1;
dl_carrierBandwidth = 106;
#initialDownlinkBWP
#genericParameters
# this is RBstart=27,L=48 (275*(L-1))+RBstart
initialDLBWPlocationAndBandwidth = 12956; # 6366 12925 12956 28875 12952
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialDLBWPsubcarrierSpacing = 1;
#pdcch-ConfigCommon
initialDLBWPcontrolResourceSetZero = 12;
initialDLBWPsearchSpaceZero = 0;
#uplinkConfigCommon
#frequencyInfoUL
ul_frequencyBand = 78;
#scs-SpecificCarrierList
ul_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
ul_subcarrierSpacing = 1;
ul_carrierBandwidth = 106;
pMax = 20;
#initialUplinkBWP
#genericParameters
initialULBWPlocationAndBandwidth = 12956;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialULBWPsubcarrierSpacing = 1;
#rach-ConfigCommon
#rach-ConfigGeneric
prach_ConfigurationIndex = 98;
#prach_msg1_FDM
#0 = one, 1=two, 2=four, 3=eight
prach_msg1_FDM = 0;
prach_msg1_FrequencyStart = 0;
zeroCorrelationZoneConfig = 12;
preambleReceivedTargetPower = -96;
#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200)
preambleTransMax = 6;
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
# ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 3;
#oneHalf (0..15) 4,8,12,16,...60,64
ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15;
#ra_ContentionResolutionTimer
#(0..7) 8,16,24,32,40,48,56,64
ra_ContentionResolutionTimer = 7;
rsrp_ThresholdSSB = 19;
#prach-RootSequenceIndex_PR
#1 = 839, 2 = 139
prach_RootSequenceIndex_PR = 2;
prach_RootSequenceIndex = 1;
# SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex
#
msg1_SubcarrierSpacing = 1,
# restrictedSetConfig
# 0=unrestricted, 1=restricted type A, 2=restricted type B
restrictedSetConfig = 0,
msg3_DeltaPreamble = 1;
p0_NominalWithGrant =-90;
# pucch-ConfigCommon setup :
# pucchGroupHopping
# 0 = neither, 1= group hopping, 2=sequence hopping
pucchGroupHopping = 0;
hoppingId = 40;
p0_nominal = -70;
# ssb_PositionsInBurs_BitmapPR
# 1=short, 2=medium, 3=long
ssb_PositionsInBurst_PR = 2;
ssb_PositionsInBurst_Bitmap = 1;
# ssb_periodicityServingCell
# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
ssb_periodicityServingCell = 2;
# dmrs_TypeA_position
# 0 = pos2, 1 = pos3
dmrs_TypeA_Position = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
subcarrierSpacing = 1;
#tdd-UL-DL-ConfigurationCommon
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
referenceSubcarrierSpacing = 1;
# pattern1
# dl_UL_TransmissionPeriodicity
# 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10
dl_UL_TransmissionPeriodicity = 6;
nrofDownlinkSlots = 7;
nrofDownlinkSymbols = 6;
nrofUplinkSlots = 2;
nrofUplinkSymbols = 4;
ssPBCH_BlockPower = -25;
}
);
# Dedicated Serving Cell Configuration
servingCellConfigDedicated = ({
# BWP-Downlink
# BWP 1 Configuration
dl_bwp-Id_1 = 1;
dl_bwp1_locationAndBandwidth = 28875; // RBstart=0, L=106 (40 MHz BW)
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
dl_bwp1_subcarrierSpacing = 1;
# BWP 2 Configuration
dl_bwp-Id_2 = 2;
dl_bwp2_locationAndBandwidth = 13750; // RBstart=0, L=51 (20 MHz BW)
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
dl_bwp2_subcarrierSpacing = 1;
# BWP 3 Configuration
dl_bwp-Id_3 = 3;
dl_bwp3_locationAndBandwidth = 6325; // RBstart=0, L=24 (10 MHz BW)
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
dl_bwp3_subcarrierSpacing = 1;
firstActiveDownlinkBWP-Id = 1; #BWP-Id
defaultDownlinkBWP-Id = 1; #BWP-Id
# bwp-InactivityTimer ENUMERATED {ms2, ms3, ms4, ms5, ms6, ms8, ms10, ms20, ms30,
# ms40,ms50, ms60, ms80,ms100, ms200,ms300, ms500,
# ms750, ms1280, ms1920, ms2560, spare10, spare9, spare8,
# spare7, spare6, spare5, spare4, spare3, spare2, spare1 }
# UplinkConfig
# BWP-Uplink
# BWP 1 Configuration
ul_bwp-Id_1 = 1;
ul_bwp1_locationAndBandwidth = 28875; // RBstart=0, L=106 (40 MHz BW)
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
ul_bwp1_subcarrierSpacing = 1;
# BWP 2 Configuration
ul_bwp-Id_2 = 2;
ul_bwp2_locationAndBandwidth = 13750; // RBstart=0, L=51 (20 MHz BW)
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
ul_bwp2_subcarrierSpacing = 1;
# BWP 3 Configuration
ul_bwp-Id_3 = 3;
ul_bwp3_locationAndBandwidth = 6325; // RBstart=0, L=24 (10 MHz BW)
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
ul_bwp3_subcarrierSpacing = 1;
firstActiveUplinkBWP-Id = 1; #BWP-Id
}
);
# ------- SCTP definitions
SCTP :
{
# Number of streams to use in input/output
SCTP_INSTREAMS = 2;
SCTP_OUTSTREAMS = 2;
};
////////// AMF parameters:
amf_ip_address = ({ipv4 = "192.168.71.132";});
NETWORK_INTERFACES :
{
GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.71.140";
GNB_IPV4_ADDRESS_FOR_NGU = "192.168.71.140";
GNB_PORT_FOR_S1U = 2152; # Spec 2152
};
}
);
MACRLCs = (
{
num_cc = 1;
tr_s_preference = "local_L1";
tr_n_preference = "local_RRC";
#pusch_TargetSNRx10 = 150;
#pucch_TargetSNRx10 = 200;
ulsch_max_frame_inactivity = 0;
dl_max_mcs = 16; # there are retransmissions if more
}
);
L1s = (
{
num_cc = 1;
tr_n_preference = "local_mac";
prach_dtx_threshold = 120;
#pucch0_dtx_threshold = 120;
}
);
RUs = (
{
local_rf = "yes"
nb_tx = 2
nb_rx = 2
att_tx = 0
att_rx = 0;
bands = [78];
max_pdschReferenceSignalPower = -27;
max_rxgain = 75;
eNB_instances = [0];
##beamforming 1x2 matrix: 1 layer x 2 antennas
bf_weights = [0x00007fff, 0x0000];
#clock_src = "internal";
# sdr_addrs = "mgmt_addr=192.168.10.2,addr=192.168.10.2,clock_source=internal,time_source=internal"
sdr_addrs = "serial=XXXXXXX";
}
);
THREAD_STRUCT = (
{
#three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT"
parallel_config = "PARALLEL_SINGLE_THREAD";
#two option for worker "WORKER_DISABLE" or "WORKER_ENABLE"
worker_config = "WORKER_ENABLE";
}
);
rfsimulator :
{
serveraddr = "server";
};
security = {
# preferred ciphering algorithms
# the first one of the list that an UE supports in chosen
# valid values: nea0, nea1, nea2, nea3
ciphering_algorithms = ( "nea0" );
# preferred integrity algorithms
# the first one of the list that an UE supports in chosen
# valid values: nia0, nia1, nia2, nia3
integrity_algorithms = ( "nia2", "nia0" );
# setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter
# what 'ciphering_algorithms' configures; same thing for 'drb_integrity'
drb_ciphering = "yes";
drb_integrity = "no";
};
log_config :
{
global_log_level ="info";
hw_log_level ="info";
phy_log_level ="info";
mac_log_level ="info";
rlc_log_level ="info";
pdcp_log_level ="info";
rrc_log_level ="info";
ngap_log_level ="debug";
f1ap_log_level ="debug";
};
@@ -150,12 +150,12 @@ gNBs =
}; };
////////// AMF parameters: ////////// AMF parameters:
amf_ip_address = ({ ipv4 = "192.168.70.132"; }); amf_ip_address = ({ ipv4 = "192.168.71.132"; });
NETWORK_INTERFACES : NETWORK_INTERFACES :
{ {
GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.151"; GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.71.140";
GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.151"; GNB_IPV4_ADDRESS_FOR_NGU = "192.168.71.140";
GNB_PORT_FOR_S1U = 2152; # Spec 2152 GNB_PORT_FOR_S1U = 2152; # Spec 2152
}; };
@@ -169,6 +169,7 @@ MACRLCs = (
tr_n_preference = "local_RRC"; tr_n_preference = "local_RRC";
pusch_TargetSNRx10 = 200; pusch_TargetSNRx10 = 200;
pucch_TargetSNRx10 = 200; pucch_TargetSNRx10 = 200;
min_grant_prb = 1;
} }
); );
@@ -230,7 +231,7 @@ log_config : {
}; };
e2_agent = { e2_agent = {
near_ric_ip_addr = "192.168.70.150"; near_ric_ip_addr = "192.168.71.21";
#sm_dir = "/path/where/the/SMs/are/located/" #sm_dir = "/path/where/the/SMs/are/located/"
sm_dir = "/usr/local/lib/flexric/" sm_dir = "/usr/local/lib/flexric/"
}; };
@@ -1,5 +1,12 @@
Active_gNBs: Active_gNBs:
- gnb-rfsim - gnb-rfsim
e2_agent:
enabled: "yes"
near_ric_ip_addr: 192.168.71.21
e2ap_server_port: 36422
sm_dir: "/usr/local/lib/flexric/"
# Asn1_verbosity, choice in: none, info, annoying # Asn1_verbosity, choice in: none, info, annoying
Asn1_verbosity: none Asn1_verbosity: none
gNBs: gNBs:
@@ -139,13 +146,13 @@ gNBs:
GNB_IPV4_ADDRESS_FOR_NGU: 192.168.71.140 GNB_IPV4_ADDRESS_FOR_NGU: 192.168.71.140
GNB_PORT_FOR_S1U: 2152 # Spec 2152 GNB_PORT_FOR_S1U: 2152 # Spec 2152
MACRLCs: MACRLCs:
- num_cc: 1 - num_cc: 1
tr_s_preference: local_L1 tr_s_preference: local_L1
tr_n_preference: local_RRC tr_n_preference: local_RRC
pusch_TargetSNRx10: 200 pusch_TargetSNRx10: 200
pucch_TargetSNRx10: 200 pucch_TargetSNRx10: 200
min_grant_prb: 5
stats_max_ue: 17 stats_max_ue: 17
L1s: L1s:
@@ -193,6 +200,6 @@ log_config:
mac_log_level: info mac_log_level: info
rlc_log_level: info rlc_log_level: info
pdcp_log_level: info pdcp_log_level: info
rrc_log_level: info rrc_log_level: debug
ngap_log_level: debug ngap_log_level: debug
f1ap_log_level: debug f1ap_log_level: debug
@@ -1,7 +1,14 @@
Active_gNBs = ( "gNB-OAI"); Active_gNBs = ("gNB-OAI");
# Asn1_verbosity, choice in: none, info, annoying # Asn1_verbosity, choice in: none, info, annoying
Asn1_verbosity = "none"; Asn1_verbosity = "none";
e2_agent = {
enabled = "yes";
near_ric_ip_addr = "192.168.71.21";
near_ric_port = 36421;
sm_dir = "/usr/local/lib/flexric/";
}
gNBs = gNBs =
( (
{ {
@@ -198,7 +205,7 @@ RUs = (
max_rxgain = 75; max_rxgain = 75;
eNB_instances = [0]; eNB_instances = [0];
#clock_src = "internal"; #clock_src = "internal";
sdr_addrs = "mgmt_addr=192.168.10.2,addr=192.168.10.2,clock_source=internal,time_source=internal" # sdr_addrs = "mgmt_addr=192.168.10.2,addr=192.168.10.2,clock_source=internal,time_source=internal"
} }
); );
@@ -206,7 +213,7 @@ RUs = (
rfsimulator : rfsimulator :
{ {
serveraddr = "server"; serveraddr = "server";
serverport = 4043; # serverport = 4043;
options = (); #("saviq"); or/and "chanmod" options = (); #("saviq"); or/and "chanmod"
modelname = "AWGN"; modelname = "AWGN";
IQfile = "/tmp/rfsimulator.iqs"; IQfile = "/tmp/rfsimulator.iqs";
@@ -0,0 +1,279 @@
Active_gNBs = ("gNB-Eurecom-5GNRBox");
# Asn1_verbosity, choice in: none, info, annoying
Asn1_verbosity = "none";
e2_agent = {
enabled = "yes";
near_ric_ip_addr = "192.168.71.21";
sm_dir = "/usr/local/lib/flexric/"
e2ap_server_port: 36422
};
gNBs =
(
{
////////// Identification parameters:
gNB_ID = 0xe00;
gNB_name = "gNB-Eurecom-5GNRBox";
// Tracking area code, 0x0000 and 0xfffe are reserved values
tracking_area_code = 1;
plmn_list = ({ mcc = 208; mnc = 99; mnc_length = 2; snssaiList = ({ sst = 1 }) });
nr_cellid = 12345678L;
////////// Physical parameters:
pdsch_AntennaPorts_XP = 2;
pdsch_AntennaPorts_N1 = 2;
pusch_AntennaPorts = 4;
do_CSIRS = 1;
do_SRS = 1;
servingCellConfigCommon = (
{
#spCellConfigCommon
physCellId = 0;
# downlinkConfigCommon
#frequencyInfoDL
# this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP)
absoluteFrequencySSB = 641280;
dl_frequencyBand = 78;
# this is 3600 MHz
dl_absoluteFrequencyPointA = 640008;
#scs-SpecificCarrierList
dl_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
dl_subcarrierSpacing = 1;
dl_carrierBandwidth = 106;
#initialDownlinkBWP
#genericParameters
# this is RBstart=27,L=48 (275*(L-1))+RBstart
initialDLBWPlocationAndBandwidth = 28875; # 6366 12925 12956 28875 12952
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialDLBWPsubcarrierSpacing = 1;
#pdcch-ConfigCommon
initialDLBWPcontrolResourceSetZero = 12;
initialDLBWPsearchSpaceZero = 0;
#uplinkConfigCommon
#frequencyInfoUL
ul_frequencyBand = 78;
#scs-SpecificCarrierList
ul_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
ul_subcarrierSpacing = 1;
ul_carrierBandwidth = 106;
pMax = 20;
#initialUplinkBWP
#genericParameters
initialULBWPlocationAndBandwidth = 28875;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialULBWPsubcarrierSpacing = 1;
#rach-ConfigCommon
#rach-ConfigGeneric
prach_ConfigurationIndex = 98;
#prach_msg1_FDM
#0 = one, 1=two, 2=four, 3=eight
prach_msg1_FDM = 0;
prach_msg1_FrequencyStart = 0;
zeroCorrelationZoneConfig = 13;
preambleReceivedTargetPower = -96;
#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200)
preambleTransMax = 6;
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;
#oneHalf (0..15) 4,8,12,16,...60,64
ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 14;
#ra_ContentionResolutionTimer
#(0..7) 8,16,24,32,40,48,56,64
ra_ContentionResolutionTimer = 7;
rsrp_ThresholdSSB = 19;
#prach-RootSequenceIndex_PR
#1 = 839, 2 = 139
prach_RootSequenceIndex_PR = 2;
prach_RootSequenceIndex = 1;
# SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex
#
msg1_SubcarrierSpacing = 1,
# restrictedSetConfig
# 0=unrestricted, 1=restricted type A, 2=restricted type B
restrictedSetConfig = 0,
msg3_DeltaPreamble = 1;
p0_NominalWithGrant =-90;
# pucch-ConfigCommon setup :
# pucchGroupHopping
# 0 = neither, 1= group hopping, 2=sequence hopping
pucchGroupHopping = 0;
hoppingId = 40;
p0_nominal = -90;
# ssb_PositionsInBurs_BitmapPR
# 1=short, 2=medium, 3=long
ssb_PositionsInBurst_PR = 2;
ssb_PositionsInBurst_Bitmap = 1;
# ssb_periodicityServingCell
# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
ssb_periodicityServingCell = 2;
# dmrs_TypeA_position
# 0 = pos2, 1 = pos3
dmrs_TypeA_Position = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
subcarrierSpacing = 1;
#tdd-UL-DL-ConfigurationCommon
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
referenceSubcarrierSpacing = 1;
# pattern1
# dl_UL_TransmissionPeriodicity
# 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10
dl_UL_TransmissionPeriodicity = 6;
nrofDownlinkSlots = 7;
nrofDownlinkSymbols = 6;
nrofUplinkSlots = 2;
nrofUplinkSymbols = 4;
ssPBCH_BlockPower = -25;
}
);
# ------- SCTP definitions
SCTP :
{
# Number of streams to use in input/output
SCTP_INSTREAMS = 2;
SCTP_OUTSTREAMS = 2;
};
////////// AMF parameters:
amf_ip_address = ({ ipv4 = "192.168.70.132";});
NETWORK_INTERFACES :
{
GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.140/24";
GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.140/24";
GNB_PORT_FOR_S1U = 2152; # Spec 2152
};
}
);
MACRLCs = (
{
num_cc = 1;
tr_s_preference = "local_L1";
tr_n_preference = "local_RRC";
pusch_TargetSNRx10 = 150;
pucch_TargetSNRx10 = 200;
}
);
L1s = (
{
num_cc = 1;
tr_n_preference = "local_mac";
pusch_proc_threads = 8;
prach_dtx_threshold = 120;
pucch0_dtx_threshold = 150;
ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0
}
);
RUs = (
{
local_rf = "yes"
nb_tx = 4
nb_rx = 4
att_tx = 0
att_rx = 0;
bands = [78];
max_pdschReferenceSignalPower = -27;
max_rxgain = 114;
eNB_instances = [0];
#beamforming 1x4 matrix:
bf_weights = [0x00007fff, 0x0000, 0x0000, 0x0000, 0x00000000, 0x00007fff, 0x0000, 0x0000, 0x0000, 0x0000, 0x00007fff, 0x0000, 0x0000, 0x0000, 0x0000, 0x00007fff];
clock_src = "internal";
}
);
THREAD_STRUCT = (
{
#three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT"
parallel_config = "PARALLEL_SINGLE_THREAD";
#two option for worker "WORKER_DISABLE" or "WORKER_ENABLE"
worker_config = "WORKER_ENABLE";
}
);
rfsimulator :
{
serveraddr = "server";
serverport = "4043";
options = (); #("saviq"); or/and "chanmod"
modelname = "AWGN";
IQfile = "/tmp/rfsimulator.iqs";
};
security = {
# preferred ciphering algorithms
# the first one of the list that an UE supports in chosen
# valid values: nea0, nea1, nea2, nea3
ciphering_algorithms = ( "nea0" );
# preferred integrity algorithms
# the first one of the list that an UE supports in chosen
# valid values: nia0, nia1, nia2, nia3
integrity_algorithms = ( "nia2", "nia0" );
# setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter
# what 'ciphering_algorithms' configures; same thing for 'drb_integrity'
drb_ciphering = "yes";
drb_integrity = "no";
};
log_config :
{
global_log_level ="info";
global_log_verbosity ="medium";
hw_log_level ="info";
hw_log_verbosity ="medium";
phy_log_level ="info";
phy_log_verbosity ="medium";
mac_log_level ="info";
mac_log_verbosity ="high";
rlc_log_level ="info";
rlc_log_verbosity ="medium";
pdcp_log_level ="info";
pdcp_log_verbosity ="medium";
rrc_log_level ="info";
rrc_log_verbosity ="medium";
ngap_log_level ="info";
ngap_log_verbosity ="medium";
};
@include "channelmod_rfsimu.conf"
@@ -0,0 +1,279 @@
Active_gNBs = ( "gNB-OAI");
# Asn1_verbosity, choice in: none, info, annoying
Asn1_verbosity = "none";
gNBs =
(
{
////////// Identification parameters:
gNB_ID = 0xe00;
gNB_name = "gNB-OAI";
// Tracking area code, 0x0000 and 0xfffe are reserved values
tracking_area_code = 1;
plmn_list = ({ mcc = 001; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1; }) });
nr_cellid = 12345678L;
////////// Physical parameters:
pdsch_AntennaPorts_XP = 2;
pdsch_AntennaPorts_N1 = 1;
pusch_AntennaPorts = 2;
do_CSIRS = 1;
do_SRS = 1;
servingCellConfigCommon = (
{
#spCellConfigCommon
physCellId = 0;
# downlinkConfigCommon
#frequencyInfoDL
# this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP)
absoluteFrequencySSB = 641280;
dl_frequencyBand = 78;
# this is 3600 MHz
dl_absoluteFrequencyPointA = 640008;
#scs-SpecificCarrierList
dl_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
dl_subcarrierSpacing = 1;
dl_carrierBandwidth = 106;
#initialDownlinkBWP
#genericParameters
# this is RBstart=27,L=48 (275*(L-1))+RBstart
initialDLBWPlocationAndBandwidth = 28875; # 6366 12925 12956 28875 12952
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialDLBWPsubcarrierSpacing = 1;
#pdcch-ConfigCommon
initialDLBWPcontrolResourceSetZero = 12;
initialDLBWPsearchSpaceZero = 0;
#uplinkConfigCommon
#frequencyInfoUL
ul_frequencyBand = 78;
#scs-SpecificCarrierList
ul_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
ul_subcarrierSpacing = 1;
ul_carrierBandwidth = 106;
pMax = 20;
#initialUplinkBWP
#genericParameters
initialULBWPlocationAndBandwidth = 28875;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialULBWPsubcarrierSpacing = 1;
#rach-ConfigCommon
#rach-ConfigGeneric
prach_ConfigurationIndex = 98;
#prach_msg1_FDM
#0 = one, 1=two, 2=four, 3=eight
prach_msg1_FDM = 0;
prach_msg1_FrequencyStart = 0;
zeroCorrelationZoneConfig = 13;
preambleReceivedTargetPower = -96;
#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200)
preambleTransMax = 6;
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;
#oneHalf (0..15) 4,8,12,16,...60,64
ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 14;
#ra_ContentionResolutionTimer
#(0..7) 8,16,24,32,40,48,56,64
ra_ContentionResolutionTimer = 7;
rsrp_ThresholdSSB = 19;
#prach-RootSequenceIndex_PR
#1 = 839, 2 = 139
prach_RootSequenceIndex_PR = 2;
prach_RootSequenceIndex = 1;
# SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex
#
msg1_SubcarrierSpacing = 1,
# restrictedSetConfig
# 0=unrestricted, 1=restricted type A, 2=restricted type B
restrictedSetConfig = 0,
msg3_DeltaPreamble = 1;
p0_NominalWithGrant =-90;
# pucch-ConfigCommon setup :
# pucchGroupHopping
# 0 = neither, 1= group hopping, 2=sequence hopping
pucchGroupHopping = 0;
hoppingId = 40;
p0_nominal = -90;
# ssb_PositionsInBurs_BitmapPR
# 1=short, 2=medium, 3=long
ssb_PositionsInBurst_PR = 2;
ssb_PositionsInBurst_Bitmap = 1;
# ssb_periodicityServingCell
# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
ssb_periodicityServingCell = 2;
# dmrs_TypeA_position
# 0 = pos2, 1 = pos3
dmrs_TypeA_Position = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
subcarrierSpacing = 1;
#tdd-UL-DL-ConfigurationCommon
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
referenceSubcarrierSpacing = 1;
# pattern1
# dl_UL_TransmissionPeriodicity
# 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10
dl_UL_TransmissionPeriodicity = 6;
nrofDownlinkSlots = 7;
nrofDownlinkSymbols = 6;
nrofUplinkSlots = 2;
nrofUplinkSymbols = 4;
ssPBCH_BlockPower = -25;
}
);
# ------- SCTP definitions
SCTP :
{
# Number of streams to use in input/output
SCTP_INSTREAMS = 2;
SCTP_OUTSTREAMS = 2;
};
////////// AMF parameters:
amf_ip_address = ( { ipv4 = "192.168.70.132";
ipv6 = "192:168:30::17";
active = "yes";
preference = "ipv4";
}
);
NETWORK_INTERFACES :
{
GNB_INTERFACE_NAME_FOR_NG_AMF = "demo-oai";
GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.129/24";
GNB_INTERFACE_NAME_FOR_NGU = "demo-oai";
GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.129/24";
GNB_PORT_FOR_S1U = 2152; # Spec 2152
};
}
);
MACRLCs = (
{
num_cc = 1;
tr_s_preference = "local_L1";
tr_n_preference = "local_RRC";
pusch_TargetSNRx10 = 150;
pucch_TargetSNRx10 = 200;
ulsch_max_frame_inactivity = 0;
ul_max_mcs = 28;
}
);
L1s = (
{
num_cc = 1;
tr_n_preference = "local_mac";
prach_dtx_threshold = 120;
pucch0_dtx_threshold = 100;
ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0
}
);
RUs = (
{
local_rf = "yes"
nb_tx = 2
nb_rx = 2
att_tx = 12;
att_rx = 12;
bands = [78];
max_pdschReferenceSignalPower = -27;
max_rxgain = 114;
eNB_instances = [0];
#beamforming 1x4 matrix:
# bf_weights = [0x00007fff, 0x0000, 0x0000, 0x00007fff];
clock_src = "internal";
}
);
THREAD_STRUCT = (
{
#three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT"
parallel_config = "PARALLEL_SINGLE_THREAD";
#two option for worker "WORKER_DISABLE" or "WORKER_ENABLE"
worker_config = "WORKER_ENABLE";
}
);
rfsimulator :
{
serveraddr = "server";
serverport = "4043";
options = (); #("saviq"); or/and "chanmod"
modelname = "AWGN";
IQfile = "/tmp/rfsimulator.iqs";
};
security = {
# preferred ciphering algorithms
# the first one of the list that an UE supports in chosen
# valid values: nea0, nea1, nea2, nea3
ciphering_algorithms = ( "nea0" );
# preferred integrity algorithms
# the first one of the list that an UE supports in chosen
# valid values: nia0, nia1, nia2, nia3
integrity_algorithms = ( "nia2", "nia0" );
# setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter
# what 'ciphering_algorithms' configures; same thing for 'drb_integrity'
drb_ciphering = "yes";
drb_integrity = "no";
};
log_config :
{
global_log_level ="info";
hw_log_level ="info";
phy_log_level ="info";
mac_log_level ="info";
rlc_log_level ="info";
pdcp_log_level ="info";
rrc_log_level ="info";
ngap_log_level ="debug";
f1ap_log_level ="debug";
};
e2_agent = {
near_ric_ip_addr = "127.0.0.1";
#sm_dir = "/path/where/the/SMs/are/located/"
sm_dir = "/usr/local/lib/flexric/"
};
@include "channelmod_rfsimu.conf"
@@ -0,0 +1,273 @@
Active_gNBs = ( "gNB-OAI");
# Asn1_verbosity, choice in: none, info, annoying
Asn1_verbosity = "none";
e2_agent = {
enabled = "yes";
near_ric_ip_addr = "192.168.71.21";
sm_dir = "/usr/local/lib/flexric/"
e2ap_server_port: 36422
};
gNBs =
(
{
////////// Identification parameters:
gNB_ID = 0xe00;
gNB_name = "gNB-OAI";
// Tracking area code, 0x0000 and 0xfffe are reserved values
tracking_area_code = 1;
plmn_list = ({ mcc = 001; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1; }) });
nr_cellid = 12345678L;
////////// Physical parameters:
pdsch_AntennaPorts_XP = 2;
pdsch_AntennaPorts_N1 = 2;
pusch_AntennaPorts = 2;
do_CSIRS = 1;
do_SRS = 1;
servingCellConfigCommon = (
{
#spCellConfigCommon
physCellId = 0;
# downlinkConfigCommon
#frequencyInfoDL
# this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP)
absoluteFrequencySSB = 641280;
dl_frequencyBand = 78;
# this is 3600 MHz
dl_absoluteFrequencyPointA = 640008;
#scs-SpecificCarrierList
dl_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
dl_subcarrierSpacing = 1;
dl_carrierBandwidth = 106;
#initialDownlinkBWP
#genericParameters
# this is RBstart=27,L=48 (275*(L-1))+RBstart
initialDLBWPlocationAndBandwidth = 28875; # 6366 12925 12956 28875 12952
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialDLBWPsubcarrierSpacing = 1;
#pdcch-ConfigCommon
initialDLBWPcontrolResourceSetZero = 12;
initialDLBWPsearchSpaceZero = 0;
#uplinkConfigCommon
#frequencyInfoUL
ul_frequencyBand = 78;
#scs-SpecificCarrierList
ul_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
ul_subcarrierSpacing = 1;
ul_carrierBandwidth = 106;
pMax = 20;
#initialUplinkBWP
#genericParameters
initialULBWPlocationAndBandwidth = 28875;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialULBWPsubcarrierSpacing = 1;
#rach-ConfigCommon
#rach-ConfigGeneric
prach_ConfigurationIndex = 98;
#prach_msg1_FDM
#0 = one, 1=two, 2=four, 3=eight
prach_msg1_FDM = 0;
prach_msg1_FrequencyStart = 0;
zeroCorrelationZoneConfig = 13;
preambleReceivedTargetPower = -96;
#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200)
preambleTransMax = 6;
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 1;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 4;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;
#oneHalf (0..15) 4,8,12,16,...60,64
ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 14;
#ra_ContentionResolutionTimer
#(0..7) 8,16,24,32,40,48,56,64
ra_ContentionResolutionTimer = 7;
rsrp_ThresholdSSB = 19;
#prach-RootSequenceIndex_PR
#1 = 839, 2 = 139
prach_RootSequenceIndex_PR = 2;
prach_RootSequenceIndex = 1;
# SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex
#
msg1_SubcarrierSpacing = 1,
# restrictedSetConfig
# 0=unrestricted, 1=restricted type A, 2=restricted type B
restrictedSetConfig = 0,
msg3_DeltaPreamble = 1;
p0_NominalWithGrant =-90;
# pucch-ConfigCommon setup :
# pucchGroupHopping
# 0 = neither, 1= group hopping, 2=sequence hopping
pucchGroupHopping = 0;
hoppingId = 40;
p0_nominal = -90;
# ssb_PositionsInBurs_BitmapPR
# 1=short, 2=medium, 3=long
ssb_PositionsInBurst_PR = 2;
ssb_PositionsInBurst_Bitmap = 1;
# ssb_periodicityServingCell
# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
ssb_periodicityServingCell = 2;
# dmrs_TypeA_position
# 0 = pos2, 1 = pos3
dmrs_TypeA_Position = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
subcarrierSpacing = 1;
#tdd-UL-DL-ConfigurationCommon
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
referenceSubcarrierSpacing = 1;
# pattern1
# dl_UL_TransmissionPeriodicity
# 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10
dl_UL_TransmissionPeriodicity = 6;
nrofDownlinkSlots = 7;
nrofDownlinkSymbols = 6;
nrofUplinkSlots = 2;
nrofUplinkSymbols = 4;
ssPBCH_BlockPower = -25;
}
);
# ------- SCTP definitions
SCTP :
{
# Number of streams to use in input/output
SCTP_INSTREAMS = 2;
SCTP_OUTSTREAMS = 2;
};
////////// AMF parameters:
amf_ip_address = ({ ipv4 = "192.168.71.132";});
NETWORK_INTERFACES :
{
GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.71.140/24";
GNB_IPV4_ADDRESS_FOR_NGU = "192.168.71.140/24";
GNB_PORT_FOR_S1U = 2152;
};
}
);
MACRLCs = (
{
num_cc = 1;
tr_s_preference = "local_L1";
tr_n_preference = "local_RRC";
pusch_TargetSNRx10 = 150;
pucch_TargetSNRx10 = 200;
ulsch_max_frame_inactivity = 0;
ul_max_mcs = 28;
}
);
L1s = (
{
num_cc = 1;
tr_n_preference = "local_mac";
prach_dtx_threshold = 120;
pucch0_dtx_threshold = 100;
ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0
}
);
RUs = (
{
local_rf = "yes"
nb_tx = 4
nb_rx = 2
att_tx = 12;
att_rx = 12;
bands = [78];
max_pdschReferenceSignalPower = -27;
max_rxgain = 114;
eNB_instances = [0];
#beamforming 1x4 matrix:
# bf_weights = [0x00010000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00010000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00010000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00010000];
# bf_weights = [0x00007fff, 0x0000, 0x0000, 0x0000, 0x00000000, 0x00007fff, 0x0000, 0x0000, 0x0000, 0x0000, 0x00007fff, 0x0000, 0x0000, 0x0000, 0x0000, 0x00007fff];
clock_src = "internal";
}
);
THREAD_STRUCT = (
{
#three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT"
parallel_config = "PARALLEL_SINGLE_THREAD";
#two option for worker "WORKER_DISABLE" or "WORKER_ENABLE"
worker_config = "WORKER_ENABLE";
}
);
rfsimulator :
{
serveraddr = "server";
serverport = "4043";
options = (); #("saviq"); or/and "chanmod"
modelname = "AWGN";
IQfile = "/tmp/rfsimulator.iqs";
};
security = {
# preferred ciphering algorithms
# the first one of the list that an UE supports in chosen
# valid values: nea0, nea1, nea2, nea3
ciphering_algorithms = ( "nea0" );
# preferred integrity algorithms
# the first one of the list that an UE supports in chosen
# valid values: nia0, nia1, nia2, nia3
integrity_algorithms = ( "nia2", "nia0" );
# setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter
# what 'ciphering_algorithms' configures; same thing for 'drb_integrity'
drb_ciphering = "yes";
drb_integrity = "no";
};
log_config :
{
global_log_level ="info";
hw_log_level ="info";
phy_log_level ="info";
mac_log_level ="info";
rlc_log_level ="info";
pdcp_log_level ="info";
rrc_log_level ="info";
ngap_log_level ="debug";
f1ap_log_level ="debug";
};
@include "channelmod_rfsimu.conf"
@@ -0,0 +1,52 @@
log_config = {
global_log_level ="info";
hw_log_level ="info";
phy_log_level ="info";
mac_log_level ="info";
rlc_log_level ="info";
pdcp_log_level ="info";
rrc_log_level ="info";
};
uicc0 = {
imsi = "001010000000002";
key = "fec86ba6eb707ed08905757b1bb44b8f";
opc= "C42449363BBAD02B66D16BC975D77CC1";
dnn= "oai";
nssai_sst=1;
nssai_sd=16777215;
}
MACRLCs = (
{
num_cc = 1;
tr_n_preference = "nfapi";
local_n_if_name = "eno1";
remote_n_address = "127.0.0.1"; //Proxy IP
local_n_address = "127.0.0.1";
local_n_portc = 50600;
remote_n_portc = 50601;
local_n_portd = 50610;
remote_n_portd = 50611;
}
);
RUs = (
{
local_rf = "yes"
nb_tx = 1
nb_rx = 1
att_tx = 5;
att_rx = 5;
bands = [78];
# max_pdschReferenceSignalPower = -27;
# max_rxgain = 114;
# eNB_instances = [0];
#beamforming 1x4 matrix:
# bf_weights = [0x00010000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00010000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00010000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00010000];
# bf_weights = [0x00007fff, 0x0000, 0x0000, 0x0000, 0x00000000, 0x00007fff, 0x0000, 0x0000, 0x0000, 0x0000, 0x00007fff, 0x0000, 0x0000, 0x0000, 0x0000, 0x00007fff];
clock_src = "internal";
}
);
@include "channelmod_rfsimu.conf"
@@ -0,0 +1,34 @@
log_config = {
global_log_level ="info";
hw_log_level ="info";
phy_log_level ="info";
mac_log_level ="info";
rlc_log_level ="info";
pdcp_log_level ="info";
rrc_log_level ="info";
};
uicc0 = {
imsi = "001010000000001";
key = "fec86ba6eb707ed08905757b1bb44b8f";
opc= "C42449363BBAD02B66D16BC975D77CC1";
dnn= "oai";
nssai_sst=1;
nssai_sd=16777215;
}
MACRLCs = (
{
num_cc = 1;
tr_n_preference = "nfapi";
local_n_if_name = "eno1";
remote_n_address = "127.0.0.1"; //Proxy IP
local_n_address = "127.0.0.1";
local_n_portc = 50600;
remote_n_portc = 50601;
local_n_portd = 50610;
remote_n_portd = 50611;
}
);
@include "channelmod_rfsimu.conf"
+1 -2
View File
@@ -1,5 +1,4 @@
uicc0 = { uicc0 = { # imsi is omitted
imsi = "208990100001100";
key = "fec86ba6eb707ed08905757b1bb44b8f"; key = "fec86ba6eb707ed08905757b1bb44b8f";
opc= "C42449363BBAD02B66D16BC975D77CC1"; opc= "C42449363BBAD02B66D16BC975D77CC1";
dnn= "oai"; dnn= "oai";
+2 -1
View File
@@ -4,6 +4,7 @@ uicc0:
opc: C42449363BBAD02B66D16BC975D77CC1 opc: C42449363BBAD02B66D16BC975D77CC1
dnn: oai dnn: oai
nssai_sst: 1 nssai_sst: 1
pdu_type: IPV4
thread-pool: "-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1" thread-pool: "-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1"
@@ -24,7 +25,7 @@ channelmod:
- model_name: rfsimu_channel_ue0 - model_name: rfsimu_channel_ue0
type: AWGN type: AWGN
ploss_dB: 20 ploss_dB: 20
nose_power_dB: -2 noise_power_dB: -2
forgetfact: 0 forgetfact: 0
offset: 0 offset: 0
ds_tdl: 0 ds_tdl: 0
@@ -0,0 +1,9 @@
[NEAR-RIC]
NEAR_RIC_IP = 192.168.71.21
NEAR_RIC_PORT = 36422
[XAPP]
DB_DIR = /tmp/
[LOG]
LEVEL = debug
@@ -1,36 +1,3 @@
################################################################################
# Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The OpenAirInterface Software Alliance licenses this file to You under
# the OAI Public License, Version 1.1 (the "License"); you may not use this file
# except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.openairinterface.org/?page_id=698
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#-------------------------------------------------------------------------------
# For more information about the OpenAirInterface (OAI) Software Alliance:
# contact@openairinterface.org
################################################################################
# OAI CN Configuration File
### This file can be used by all OAI NFs
### Some fields are specific to an NF and will be ignored by other NFs
## NOTE ON YAML ANCHORS ##
# We use YAML anchors to ease the configuration and to avoid duplicating parts of the configuration.
# This is especially true for the SNSSAIs, as we have to define them for multiple NFs.
# Please note that the use of anchors is not mandatory, and you can define the SNSSAI in each NF yourself.
# You can read more about anchors here: https://yaml.org/spec/1.2.2/#anchors-and-aliases
############# Common configuration
# Log level for all the NFs # Log level for all the NFs
log_level: log_level:
general: debug general: debug
@@ -103,8 +70,8 @@ amf:
# This really depends on if we want to keep the "mini" version or not # This really depends on if we want to keep the "mini" version or not
support_features_options: support_features_options:
enable_simple_scenario: yes # "no" by default with the normal deployment scenarios with AMF/SMF/UPF/AUSF/UDM/UDR/NRF. enable_simple_scenario: yes # "no" by default with the normal deployment scenarios with AMF/SMF/UPF/AUSF/UDM/UDR/NRF.
# set it to "yes" to use with the minimalist deployment scenario (including only AMF/SMF/UPF) by using the internal AUSF/UDM implemented inside AMF. # set it to "yes" to use with the minimalist deployment scenario (including only AMF/SMF/UPF) by using the internal AUSF/UDM implemented inside AMF.
# There's no NRF in this scenario, SMF info is taken from "nfs" section. # There's no NRF in this scenario, SMF info is taken from "nfs" section.
enable_nssf: no enable_nssf: no
enable_smf_selection: no enable_smf_selection: no
relative_capacity: 30 relative_capacity: 30
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
[mysqld]
wait_timeout = 2
interactive_timeout = 2
+650
View File
@@ -0,0 +1,650 @@
#!/bin/bash
set -euo pipefail
# --- UE profile map (index 0..10). 0-index kept for convenience ---
# Initialize with "0" to denote no profile chosen.
declare -a UE_PROFILE_MAP
for i in {0..10}; do UE_PROFILE_MAP[$i]=0; done
# ---------------------------[ Random / diurnal Mode ]----------------------------
# UE scaler mode: 'random' (rapid random targets) or 'diurnal' (legacy cosine)
UE_SCALER_MODE="${UE_SCALER_MODE:-random}"
# Interval (sec) for scaler ticks
UE_SCALER_INTERVAL_SEC="${UE_SCALER_INTERVAL_SEC:-30}"
# -------------------------------[ Config ]------------------------------------
ARTIFACT_DIR="/media/nakaolab-go/2nd/artifacts"
mkdir -p "$ARTIFACT_DIR"
POWER_LOG_FILE="${POWER_LOG_FILE:-$ARTIFACT_DIR/power_usage.csv}"
CPU_PROXY_LOG_FILE="${CPU_PROXY_LOG_FILE:-$ARTIFACT_DIR/cpu_proxy.csv}"
RRC_SINCE_STATE="${RRC_SINCE_STATE:-$ARTIFACT_DIR/.rrc_since}"
# xApp (mac stats) container + path inside container
STATS_CONTAINER="${STATS_CONTAINER:-xapp-chatgpt}"
MAC_STATS_FILE_IN_CONTAINER="${MAC_STATS_FILE_IN_CONTAINER:-/tmp/mac_stats.log}"
PDCP_STATS_FILE_IN_CONTAINER="${PDCP_STATS_FILE_IN_CONTAINER:-/tmp/pdcp_stats.log}"
# Intervals (seconds)
RRC_LOG_INTERVAL_SEC="${RRC_LOG_INTERVAL_SEC:-10}"
POWER_LOG_INTERVAL_SEC="${POWER_LOG_INTERVAL_SEC:-5}"
CPU_PROXY_INTERVAL_SEC="${CPU_PROXY_INTERVAL_SEC:-5}"
# Docker compose helper
DC_BIN=(docker compose)
dc() { command "${DC_BIN[@]}" "$@"; }
dc down -v --remove-orphans
# Service names (override if your compose uses different ones)
AMF_SERVICE="${AMF_SERVICE:-oai-amf}"
UPF_SERVICE="${UPF_SERVICE:-oai-upf}"
GNB_SERVICE="${GNB_SERVICE:-oai-gnb}"
XAPP_SERVICE="${XAPP_SERVICE:-xapp-chatgpt}"
# UE service name pattern (for discovery)
UE_NAME_REGEX="${UE_NAME_REGEX:-^oai-nr-ue}"
# Test controls
TEST_MODE="${TEST_MODE:-0}" # 1=manual profiles allowed (TEST_* honored); 0=auto
TEST_UES="${TEST_UES:-}"
TEST_PROFILES="${TEST_PROFILES:-}" # profile name
TEST_DURATION_SEC="${TEST_DURATION_SEC:-0}" # 0 = run indefinitely
# Diurnal scaling controls (normal mode only; ignored if TEST_UE is set)
UE_MIN="${UE_MIN:-1}" # Minimum number of UEs to keep connected
UE_MAX="${UE_MAX:-10}"
UE_PEAK_HOUR="${UE_PEAK_HOUR:-17}" # Local hour with peak load (0..23)
UE_NOISE_FRAC="${UE_NOISE_FRAC:-0.15}" # ±fractional noise applied to target
# -------------------------[ Power Model (mW) ]--------------------------------
declare -A PWR_MW_PROFILE=(
[idle]=60 # screen on, modem camped, no transfers
[iot_chatty]=150 # tiny bursts, long sleeps
[browsing]=380 # short transfers with think-time
[voip]=520 # steady uplink/downlink small packets
[video]=850 # continuous DL; 720–1080p
[gaming]=950 # bursty DL + UL keepalives
[boring]=500 # for single PRB testing
[bulk_download]=1100 # sustained UL/DL at MCS ceiling
)
# -------------------------------[ Utils ]-------------------------------------
ts_now() { date +%s; }
ts_iso() { date -Is; }
ts_tag() { date +%Y%m%d_%H%M%S; }
log() { echo "[$(ts_iso)] $*"; }
exec_safe() {
local svc="$1"; shift
dc exec -T "$svc" bash -lc "$*"
}
# -----------------------------[ Discovery ]-----------------------------------
list_all_services() { dc config --services; }
get_all_ue_services() { list_all_services | grep -E "${UE_NAME_REGEX}" || true; }
get_core_services() {
# everything except UEs, gNB, xApp
list_all_services | grep -Ev "(${UE_NAME_REGEX}|^${GNB_SERVICE}$|^${XAPP_SERVICE}$)" || true
}
get_running_ues() {
dc ps --services --filter "status=running" \
| grep -E "${UE_NAME_REGEX}" || true
}
# -------------------------[ Readiness helpers ]-------------------------------
svc_cid() { dc ps -q "$1"; } # container id for a service
wait_healthy() {
local svc="$1" timeout="${2:-60}"
local cid; cid="$(svc_cid "$svc")"
if [[ -z "$cid" ]]; then
log "wait_healthy: service '$svc' not running."
return 1
fi
local health
health="$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{end}}' "$cid" 2>/dev/null || true)"
if [[ -z "$health" ]]; then
log "wait_healthy: no healthcheck for '$svc'; skipping."
return 0
fi
local start; start="$(ts_now)"
while :; do
health="$(docker inspect -f '{{.State.Health.Status}}' "$cid" 2>/dev/null || echo "")"
if [[ "$health" == "healthy" ]]; then
log "$svc is healthy."
return 0
fi
if (( $(ts_now) - start > timeout )); then
log "wait_healthy timeout for '$svc' (last='$health')."
return 1
fi
sleep 1
done
}
wait_log() {
local svc="$1" pat="$2" timeout="${3:-60}"
local cid; cid="$(svc_cid "$svc")"
if [[ -z "$cid" ]]; then
log "wait_log: service '$svc' not running."
return 1
fi
local start; start="$(ts_now)"
while :; do
if dc logs "$svc" --since=2m 2>&1 | grep -Eiq -- "$pat"; then
return 0
fi
if (( $(ts_now) - start > timeout )); then
log "wait_log timeout for '$svc' waiting '$pat'."
return 1
fi
sleep 1
done
}
get_container_ip() {
local cid; cid="$(svc_cid "$1")"
[[ -z "$cid" ]] && return 1
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$cid"
}
# --------------------------[ Staged bring-up ]--------------------------------
start_services_list() {
[[ $# -eq 0 ]] && return 0
dc up -d "$@" >/dev/null 2>&1
}
start_ue_list() {
[[ $# -eq 0 ]] && return 0
for svc in "$@"; do
log "Starting: $svc"
dc up -d "$svc" >/dev/null 2>&1
done
}
start_in_stages() {
# 1) CORE
mapfile -t core_svcs < <(get_core_services)
if [[ ${#core_svcs[@]} -gt 0 ]]; then
start_services_list "${core_svcs[@]}"
else
log "No CORE services to start."
exit 1
fi
# Ensure AMF is ready
wait_healthy "${AMF_SERVICE}" 15
wait_healthy "${UPF_SERVICE}" 15
# exec_safe "${UPF_SERVICE}" 'bash -lc "
# set -e
# sysctl -w net.ipv4.ip_forward=1
# sysctl -w net.ipv4.conf.all.rp_filter=2
# sysctl -w net.ipv4.conf.eth0.rp_filter=2
# sysctl -w net.ipv4.conf.eth1.rp_filter=2
# sysctl -w net.ipv4.conf.tun0.rp_filter=2
# ip rule add pref 100 iif tun0 to 192.168.71.0/24 lookup 201 || true
# ip route replace 192.168.71.0/24 dev eth0 table 201
# ip rule add pref 200 iif tun0 lookup 200 || true
# ip route replace 12.1.1.0/24 dev tun0 table 200
# ip route replace default via 192.168.72.129 dev eth1 table 200
# # UE -> DN (192.168.72.128/26) via eth1
# iptables -t nat -C POSTROUTING -s 12.1.1.0/24 -d 192.168.72.128/26 -o eth1 -j MASQUERADE 2>/dev/null || \
# iptables -t nat -A POSTROUTING -s 12.1.1.0/24 -d 192.168.72.128/26 -o eth1 -j MASQUERADE
# # UE -> 192.168.71.0/24 via eth0
# iptables -t nat -C POSTROUTING -s 12.1.1.0/24 -d 192.168.71.0/24 -o eth0 -j MASQUERADE 2>/dev/null || \
# iptables -t nat -A POSTROUTING -s 12.1.1.0/24 -d 192.168.71.0/24 -o eth0 -j MASQUERADE
# "'
wait_healthy oai-flexric 15
# 2) gNB
start_services_list "${GNB_SERVICE}"
wait_log "${GNB_SERVICE}" "Received NGSetupResponse from AMF" 20
wait_log "${GNB_SERVICE}" "Transaction ID E2 SETUP-REQUEST 0 E2 SETUP-RESPONSE 0" 15
# 3) xApp
start_services_list "${XAPP_SERVICE}"
wait_healthy "${XAPP_SERVICE}" 15
# 4) UE(s)
# if [[ -n "${TEST_UE:-}" ]]; then
# # Allow multiple UEs in TEST_UE (comma and/or space separated)
# IFS=', ' read -r -a _ue_list <<< "${TEST_UE}"
# start_services_list "${_ue_list[@]}"
# for ue in "${_ue_list[@]}"; do
# [[ -z "$ue" ]] && continue
# if wait_oai_tun "$ue" 20; then
# exec_safe "$ue" 'ip route replace default dev oaitun_ue1' || true
# run_traffic_profile "$ue" "random" "0" || true
# else
# exit 1
# fi
# done
# else
# # Initial diurnal target, then enforce UE count
# local target; if [[ "${UE_SCALER_MODE}" == "diurnal" ]]; then target="$(calc_target_ue_count)"; else target="$(random_target_ue_count)"; fi
# log "Initial diurnal target UE count: ${target}"
# ensure_ue_count "$target"
# fi
}
# -------------------------[ Diurnal UE scaling ]------------------------------
calc_target_ue_count() {
local total_ues="${UE_MAX:-}"
if [[ -z "$total_ues" ]]; then
total_ues="$(get_all_ue_services | wc -l | tr -d ' ')"
fi
local min="${UE_MIN:-1}"
if (( total_ues < min )); then total_ues="$min"; fi
local H M
H="$(date +%H)"; M="$(date +%M)"
# Compute target via cosine wave (peak at UE_PEAK_HOUR), then add noise and clamp
awk -v min="$min" -v max="$total_ues" -v peak="${UE_PEAK_HOUR:-14}" \
-v noise="${UE_NOISE_FRAC:-0.15}" -v H="$H" -v M="$M" 'BEGIN{
# time-of-day in days [0,1)
t = (H*60.0 + M)/1440.0
ph = peak/24.0
norm = (cos(2*3.141592653589793*(t - ph)) + 1)/2 # 0..1
base = min + norm*(max-min)
srand(); u = (rand()*2.0 - 1.0) * noise # [-noise, +noise]
val = base * (1.0 + u)
if (val < min) val = min
if (val > max) val = max
# round to nearest integer
printf("%.0f\n", val)
}'
}
ensure_ue_count() {
local target="$1"
# Build arrays
mapfile -t all_ues < <(get_all_ue_services)
mapfile -t running < <(get_running_ues)
# Current and desired counts
local cur="${#running[@]}"
if (( cur == target )); then
log "UE scaler: already at target (${target})."
return 0
fi
# Set helpers for membership tests
declare -A IS_RUNNING=()
for u in "${running[@]}"; do IS_RUNNING["$u"]=1; done
# Determine non-running UEs
declare -a not_running=()
for u in "${all_ues[@]}"; do
[[ -z "${IS_RUNNING[$u]:-}" ]] && not_running+=("$u")
done
# Start more UEs if needed
if (( cur < target )); then
local need=$(( target - cur ))
log "UE scaler: need +${need} UEs (cur=${cur}, target=${target})."
if (( ${#not_running[@]} == 0 )); then
log "UE scaler: no available UEs to start."
return 0
fi
local to_start=()
if command -v shuf >/dev/null 2>&1; then
mapfile -t to_start < <(printf '%s\n' "${not_running[@]}" | shuf -n "$need" || true)
else
to_start=("${not_running[@]:0:$need}")
fi
if (( ${#to_start[@]} > 0 )); then
start_ue_list "${to_start[@]}"
for ue in "${to_start[@]}"; do
if wait_log "$ue" "oaitun_ue1 successfully configured" 5 &&
exec_safe "$ue" 'ip link show oaitun_ue1 >/dev/null 2>&1'; then
exec_safe "$ue" 'ip route replace default dev oaitun_ue1'
# run_traffic_profile "$ue" "$(choose_random_profile)" "0"
run_traffic_profile "$ue" "boring"
else
dc down -v "$ue" >/dev/null 2>&1 &
fi
done
fi
return 0
fi
# Otherwise stop some UEs to shrink
local drop=$(( cur - target ))
log "UE scaler: need -${drop} UEs (cur=${cur}, target=${target})."
local to_stop=()
if command -v shuf >/dev/null 2>&1; then
mapfile -t to_stop < <(printf '%s\n' "${running[@]}" | shuf -n "$drop" || true)
else
to_stop=("${running[@]:0:$drop}")
fi
for ue in "${to_stop[@]}"; do
dc down -v "$ue" >/dev/null 2>&1 &
done
wait
}
# --------------------------[ UE traffic helpers ]-----------------------------
PROFILES=(boring browsing video bulk_download voip iot_chatty gaming)
choose_random_profile() {
if command -v shuf >/dev/null 2>&1; then
shuf -e "${PROFILES[@]}" -n1
else
idx=$(( RANDOM % ${#PROFILES[@]} ))
echo "${PROFILES[$idx]}"
fi
}
run_traffic_profile() {
local ue="$1" profile="$2"
# Record selected profile for this UE into UE_PROFILE_MAP
if [[ "$ue" =~ ([0-9]+)$ ]]; then
__idx="${BASH_REMATCH[1]}"
UE_PROFILE_MAP[$__idx]="$profile"
fi
dc cp /home/nakaolab-go/OAI/openairinterface5g/tools/scripts/traffic_runner.sh "$ue":/tmp/traffic_runner.sh >/dev/null 2>&1
exec_safe "$ue" "bash -lc '
prof=\$1;
/tmp/traffic_runner.sh --profile \$prof &
' -- $profile"
}
# --------------------------[ RRC / MAC scraping ]-----------------------------
rotate_stats_capture() {
local tag="${1:-}"
[[ -z "$tag" ]] && tag="$(ts_tag)"
if dc ps --services --filter "status=running" | grep -q "^${STATS_CONTAINER}$"; then
log "Rotating stats from ${STATS_CONTAINER}"
dc cp "${STATS_CONTAINER}:${MAC_STATS_FILE_IN_CONTAINER}" \
"${ARTIFACT_DIR}/mac_stats_${tag}.log" || true
dc cp "${STATS_CONTAINER}:${PDCP_STATS_FILE_IN_CONTAINER}" \
"${ARTIFACT_DIR}/pdcp_stats_${tag}.log" || true
# exec_safe "$STATS_CONTAINER" "> '${MAC_STATS_FILE_IN_CONTAINER}'"
# exec_safe "$STATS_CONTAINER" "> '${PDCP_STATS_FILE_IN_CONTAINER}'"
else
log "stats container ${STATS_CONTAINER} not running; skipping rotate"
fi
}
_since_arg_from_file() {
local f="$1"
local val
val="$(tr -d '[]\r\n' < "$f" 2>/dev/null || true)"
if [[ -n "$val" ]]; then
printf -- '--since=%s' "$val"
else
printf -- '--since=2m'
fi
}
dump_gnb_logs() {
local tag="$1"
dc logs "${GNB_SERVICE}" 2>&1 | grep -E "NR_RRC" > "${ARTIFACT_DIR}/gnb_rrc_${tag}.log"
dc logs "${GNB_SERVICE}" 2>&1 | grep -E "\[NR_MAC\] I Frame\.Slot|MAC:" > "${ARTIFACT_DIR}/gnb_${tag}.log"
}
# -----------------------------[ Power / CPU ]---------------------------------
init_power_logs() {
if [[ ! -s "$POWER_LOG_FILE" ]]; then
echo "epoch,ue,profile,power_mW,dt_s,energy_mJ" > "$POWER_LOG_FILE"
fi
if [[ ! -s "$CPU_PROXY_LOG_FILE" ]]; then
echo "epoch,container,cpu_percent,mem_usage" > "$CPU_PROXY_LOG_FILE"
fi
}
profile_total_mw() {
local profile="$1"
echo "${PWR_MW_PROFILE[$profile]:-0}"
}
sample_power_once() {
local epoch="$(ts_now)"
local dt_s="${POWER_LOG_INTERVAL_SEC}"
while IFS= read -r ue; do
[[ -z "$ue" ]] && continue
# Determine profile without container lock by consulting UE_PROFILE_MAP
local picked_profile picked_duration
if [[ "$ue" =~ ([0-9]+)$ ]]; then
__idx="${BASH_REMATCH[1]}"
picked_profile="${UE_PROFILE_MAP[$__idx]}"
fi
picked_duration=""
if [[ -z "$picked_profile" ]]; then
if [[ "${TEST_MODE}" -eq 1 ]]; then picked_profile="${TEST_PROFILE}"; else picked_profile="idle"; fi
fi
while [[ "$picked_profile" == -* ]]; do
picked_profile="${picked_profile#-}"
done
picked_profile="${picked_profile,,}"
local p_mw
p_mw="$(profile_total_mw "$picked_profile")"
: "${p_mw:=0}"
local energy_mJ=$(( p_mw * dt_s ))
echo "${epoch},${ue},${picked_profile},${p_mw},${dt_s},${energy_mJ}" >> "$POWER_LOG_FILE"
done < <(get_running_ues)
}
# --- CPU proxy: query docker stats by container ID and map back to service name ---
cpu_proxy_once() {
local epoch="$(ts_now)"
# Build list of UE service names (same as container names)
declare -a SVC_LIST=()
while IFS= read -r svc; do
[[ -z "$svc" ]] && continue
SVC_LIST+=("$svc")
done < <(get_running_ues)
# Query docker stats by NAME and key 'seen' by NAME
local fmt='{{.Name}},{{.CPUPerc}},{{.MemUsage}}'
declare -A SEEN=()
while IFS=',' read -r name cpu mem; do
[[ -z "$name" ]] && continue
name="${name#/}"
SEEN["$name"]=1
cpu="${cpu%%%}" # drop trailing '%'
echo "${epoch},${name},${cpu},${mem}" >> "$CPU_PROXY_LOG_FILE"
done < <(docker stats --no-stream --format "$fmt" "${SVC_LIST[@]}" 2>/dev/null || true)
# Any container absent from docker stats output → record zeros once
local svc
for svc in "${SVC_LIST[@]}"; do
if [[ -z "${SEEN[$svc]:-}" ]]; then
echo "${epoch},${svc},0,0" >> "$CPU_PROXY_LOG_FILE"
fi
done
}
rotate_csv_if_any() {
local src="$1" prefix="$2" tag="${3:-}"
[[ -z "$tag" ]] && tag="$(ts_tag)"
if [[ -s "$src" ]]; then
mv "$src" "${ARTIFACT_DIR}/${prefix}_${tag}.csv" || true
case "$prefix" in
power_usage) echo "epoch,ue,profile,power_mW,dt_s,energy_mJ" > "$src" ;;
cpu_proxy) echo "epoch,container,cpu_percent,mem_usage" > "$src" ;;
esac
fi
}
finalize_and_rotate_artifacts() {
local tag; tag="$(ts_tag)"
rotate_stats_capture "$tag" || true
dump_gnb_logs "$tag" || true
rotate_csv_if_any "$POWER_LOG_FILE" "power_usage" "$tag"
rotate_csv_if_any "$CPU_PROXY_LOG_FILE" "cpu_proxy" "$tag"
}
# -------------------------------[ Signals ]-----------------------------------
__SHUTTING_DOWN=0
on_interrupt() {
if [[ "${__SHUTTING_DOWN}" -eq 1 ]]; then return; fi
__SHUTTING_DOWN=1
log "Signal received (INT/TERM); taking final samples and rotating artifacts…"
# One last sample tick
sample_power_once || true
cpu_proxy_once || true
# Rotate artifacts and clean up
finalize_and_rotate_artifacts
cleanup
# 130 is the conventional exit code for SIGINT
exit 130
}
# --------------------------------[ Cleanup ]----------------------------------
cleanup() {
# Default: stop only UEs; keep infra up for inspection
log "Cleanup: stopping UE containers only"
while IFS= read -r ue; do
[[ -z "$ue" ]] && continue
dc down -v "$ue" >/dev/null 2>&1 &
done < <(dc ps --services | grep -E "${UE_NAME_REGEX}" || true)
}
# ---------------------------------[ Main ]------------------------------------
random_target_ue_count() {
local cur="$(get_running_ues | wc -l | tr -d ' ')"
bias=$((RANDOM % 100)) # 0–99
if (( cur <= UE_MIN )); then
if (( bias < 70 )); then
delta=$(( (RANDOM % 3) + 1 )) # +1..+3
else
delta=$(( RANDOM % 7 - 3 )) # -3..+3
fi
elif (( cur >= UE_MAX )); then
if (( bias < 70 )); then
delta=$(( -((RANDOM % 3) + 1) )) # -1..-3
else
delta=$(( RANDOM % 7 - 3 )) # -3..+3
fi
else # normal
delta=$(( RANDOM % 7 - 3 )) # -3..+3
fi
local target=$((cur + delta))
if (( target < UE_MIN )); then
target="${UE_MIN}"
elif (( target > UE_MAX )); then
target="${UE_MAX}"
fi
echo "$target"
}
main() {
trap on_interrupt INT TERM
init_power_logs
# Staged bring-up with readiness checks for both normal and TEST modes
start_in_stages
# Kick off traffic workload(s)
if [[ "${TEST_MODE}" -eq 1 ]]; then
log "TEST UE(S) ENABLED"
# Support multiple UEs (comma/space/newline separated)
read -r -a _ue_list <<< "$(tr ',\n' ' ' <<< "${TEST_UES}")"
read -r -a _ue_profiles <<< "$(tr ',\n' ' ' <<< "${TEST_PROFILES}")"
start_ue_list "${_ue_list[@]}"
for i in "${!_ue_list[@]}"; do
ue="${_ue_list[$i]}"
prof="${_ue_profiles[$i]}"
[[ -z "$ue" ]] && continue
if wait_log "$ue" "oaitun_ue1 successfully configured" 5 &&
exec_safe "$ue" 'ip link show oaitun_ue1 >/dev/null 2>&1'; then
exec_safe "$ue" "ip route replace default dev oaitun_ue1"
run_traffic_profile "$ue" "$prof"
else
exit 1
fi
done
fi
# Optional finite duration for TEST mode
if [[ "${TEST_MODE}" -eq 1 && -n "${TEST_UE:-}" && "${TEST_DURATION_SEC:-0}" -gt 0 ]]; then
RUN_STOP_TS=$(( $(ts_now) + TEST_DURATION_SEC ))
fi
# Main sampling & scaling loop
local last_rrc="$(( $(ts_now) - RRC_LOG_INTERVAL_SEC ))"
local last_power="$(( $(ts_now) - POWER_LOG_INTERVAL_SEC ))"
local last_cpu="$(( $(ts_now) - CPU_PROXY_INTERVAL_SEC ))"
local last_scale="$(( $(ts_now) - UE_SCALER_INTERVAL_SEC ))"
log "Entering main loop (sampling + diurnal scaler)…"
while :; do
local now; now="$(ts_now)"
if (( now - last_power >= POWER_LOG_INTERVAL_SEC )); then
sample_power_once || true
last_power="$now"
fi
if (( now - last_cpu >= CPU_PROXY_INTERVAL_SEC )); then
cpu_proxy_once || true
last_cpu="$now"
fi
# Re-evaluate diurnal target (normal mode only)
if [[ "${TEST_MODE}" -eq 0 ]] && (( now - last_scale >= UE_SCALER_INTERVAL_SEC )); then
local target;
if [[ "${UE_SCALER_MODE}" == "diurnal" ]]; then
target="$(calc_target_ue_count)";
else
target="$(random_target_ue_count)";
fi
log "Scaler tick: target = ${target}"
ensure_ue_count "$target"
last_scale="$now"
fi
# TEST duration stop gate
if [[ -n "${RUN_STOP_TS:-}" && "$now" -ge "${RUN_STOP_TS}" ]]; then
log "TEST run reached duration (${TEST_DURATION_SEC}s); finalizing artifacts…"
finalize_and_rotate_artifacts
cleanup
exit 0
fi
sleep 1
done
}
main "$@"
@@ -1,6 +0,0 @@
services:
oai-nr-ue:
privileged: true
stdin_open: true
tty: true
command: gdb --args /opt/oai-nr-ue/bin/nr-uesoftmodem -O /opt/oai-nr-ue/etc/nr-ue.conf -E --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001100 -C 3619200000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time
@@ -1,27 +0,0 @@
include:
- ../local_common_overrides/build_images.yaml
- ../local_common_overrides/rebuild_nr_softmodems.yaml
services:
oai-gnb:
privileged: true
depends_on:
rebuild-nr-softmodems:
condition: service_completed_successfully
volumes:
- ../../../cmake_targets/ran_build/build/nr-softmodem:/opt/oai-gnb/bin/nr-softmodem
- ../../../cmake_targets/ran_build/build/librfsimulator.so:/usr/local/lib/librfsimulator.so
oai-nr-ue:
privileged: true
image: ue-local
depends_on:
build-ran-build:
condition: service_completed_successfully
restart: true
oai-gnb:
condition: service_started
rebuild-nr-softmodems:
condition: service_completed_successfully
volumes:
- ../../../cmake_targets/ran_build/build/nr-uesoftmodem:/opt/oai-nr-ue/bin/nr-uesoftmodem
- ../../../cmake_targets/ran_build/build/librfsimulator.so:/usr/local/lib/librfsimulator.so
- ../../../crashdumps/:/cores/
@@ -0,0 +1,44 @@
docker build -f docker/Dockerfile.gnb_e2 -t nakaolab:sep1 .
O-RU and O-DU are connected via the Fronthaul (FH) interface.
FH traffic is logically diviced into 4 planes:
1. Control Plane which transports real-time slot configuration control messages
2. User Plane which transports IQ samples data (amplitude and phase)
3. Synchronization Plane transports timing information messages
4. Management Plane carries non-real-time management operation messages
C/U Plane messages are exchanged using the eCPRI protocol.
The energy consumption of RU can be modeled as
P = P_0 + P_RF + P_BB + P_PAM
where
P_0 : baseline components in the RU (fabric, ethernet, PCIe, etc.)
P_RF : a-to-d/d-to-a converters (ADC/DAC) and the freq up/down converters
P_BB : core digital processor (eFPGA) and the other low-PHY processing units
(e.g. beamforming) for each antenna chain
P_PAM : power amplifier module (PAM) at each antenna chain
The paper claims that components other than PAM are rather static (?)
-> I guess the PAM is controlled by RF. Thus P_RF is the minimum amount of power
required to run RF.
P_PAM = P_(PAM, 0) * P_(PAM, c)(r) * E_PAM * n_ant
where
P_(PAM, 0) : baseline power consumption that depends on the n of antennas,
ganging elements, and the maximum EIRP per antenna
* EIRP (Effective Isotropic Radiated Power) is the total power radiated
by an antenna in the direction of its maximum gain, referenced to an ideal isotropic radiator.
P_(PAM, c)(r) : a factor that depends on _the amount of subcarriers to be amplified_,
which in turn depends on the n of RBs (r) carried therein
E_PAM : captures the efficiency of the amplifier to convert DC power into signal power gain
n_ant : n of radio chains (with ONE amplifier per radio chain)
> This observation motivates the principle behind the solution we propose in this paper:
consolidate data into as few symbol periods as possible, using more radio resources per period instead.
Since keeping PAM awake consumes more energy, it is better to send data in short bursts, where each burst
utilizes more spectrum resources
MIMO Muting
Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

@@ -0,0 +1,144 @@
#!/usr/bin/env python3
"""
UE Count Plotter
----------------
Parses log lines like:
[2025-09-08 21:33:40.789676] UE RNTI=44512 ...
Groups entries into 1-second slots (by wall-clock time) and plots the number of
unique UEs (by RNTI) per slot for the entire file.
Usage:
python ue_count_plot.py /path/to/log.txt [--save output.png] [--no-show]
Notes:
- Uses matplotlib for a single-line plot.
- Does not set any colors or styles.
- For very large logs, this streams line-by-line with O(N) memory in the number
of distinct seconds encountered (each holds a set of RNTIs).
"""
import argparse
import datetime as dt
import re
import sys
from collections import defaultdict, OrderedDict
from typing import Dict, Set, Iterable, Tuple
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
TIMESTAMP_RE = re.compile(r"\[(?P<ts>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d{1,6})?)\]")
RNTI_RE = re.compile(r"\bRNTI=(?P<rnti>\d+)\b")
def parse_lines(lines: Iterable[str]) -> Dict[dt.datetime, Set[int]]:
"""
Parse lines and bucket unique RNTIs per second.
Returns:
dict mapping second (datetime truncated to seconds) -> set of RNTIs
"""
buckets: Dict[dt.datetime, Set[int]] = defaultdict(set)
for line in lines:
ts_m = TIMESTAMP_RE.search(line)
rnti_m = RNTI_RE.search(line)
if not ts_m or not rnti_m:
# Skip lines that don't fit the schema
continue
ts_str = ts_m.group("ts")
# Parse timestamp with or without fractional seconds
# Accept both "%Y-%m-%d %H:%M:%S" and with "%f"
if "." in ts_str:
ts = dt.datetime.strptime(ts_str, "%Y-%m-%d %H:%M:%S.%f")
else:
ts = dt.datetime.strptime(ts_str, "%Y-%m-%d %H:%M:%S")
# Truncate to whole-second wall time
ts_minute = ts.replace(second=0, microsecond=0)
rnti = int(rnti_m.group("rnti"))
buckets[ts_minute].add(rnti)
return buckets
def sort_and_fill(buckets: Dict[dt.datetime, Set[int]]) -> Tuple[list, list]:
"""
Sorts buckets by time and fills missing seconds with zero counts.
Returns:
times (list[datetime]), counts (list[int])
"""
if not buckets:
return [], []
# Sort keys
sorted_times = sorted(buckets.keys())
start = sorted_times[0]
end = sorted_times[-1]
all_times = []
counts = []
cur = start
one_sec = dt.timedelta(minutes=1)
while cur <= end:
all_times.append(cur)
counts.append(len(buckets.get(cur, set())))
cur += one_sec
return all_times, counts
def plot_counts(times: list, counts: list, title: str = "Unique UEs per minute"):
"""
Render a simple matplotlib line plot (no specific colors/styles set).
"""
fig, ax = plt.subplots(figsize=(10, 4))
ax.plot(times, counts, linestyle="-")
ax.set_title(title)
ax.set_xlabel("Time (s)")
ax.set_ylabel("Unique UEs")
ax.xaxis.set_major_formatter(mdates.DateFormatter("%H:%M:%S"))
fig.autofmt_xdate()
plt.tight_layout()
return fig, ax
def main():
parser = argparse.ArgumentParser(description="Plot unique UEs (by RNTI) per minute from a log file.")
parser.add_argument("--save", help="Optional path to save the plot image (e.g., output.png)")
parser.add_argument("--no-show", action="store_true", help="Do not display the plot window")
args = parser.parse_args()
# Read lines
try:
lines = open("artifacts/mac_stats_20250909_231855.log", "r", encoding="utf-8")
except OSError as e:
print(f"Error opening {args.logfile}: {e}", file=sys.stderr)
sys.exit(1)
buckets = parse_lines(lines)
if lines is not sys.stdin:
lines.close()
times, counts = sort_and_fill(buckets)
if not times:
print("No valid entries found to plot.", file=sys.stderr)
sys.exit(2)
fig, _ = plot_counts(times, counts)
if args.save:
try:
fig.savefig(args.save, dpi=150, bbox_inches="tight")
print(f"Saved plot to: {args.save}")
except Exception as e:
print(f"Failed to save figure: {e}", file=sys.stderr)
if not args.no_show:
plt.show()
if __name__ == "__main__":
main()
@@ -0,0 +1,772 @@
from __future__ import annotations
"""
Memory-safe, drop-in refined version of prediction.py
- Streams very large log files (no giant raw DataFrames)
- Computes observed flags without large Python sets
- Zero-copy feature audit (mask features on the fly)
You can run this directly. Key differences from the original are marked with
"STREAMING" and "ZERO-COPY" comments.
"""
import glob, os, re
from collections import defaultdict
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path
from typing import List, Tuple, Dict, Optional
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
# ----------------------------
# Utils
# ----------------------------
def set_seed(seed=42):
import random
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
set_seed(42)
def ensure_dir(p: Path):
p.mkdir(parents=True, exist_ok=True)
return p
def _run_lengths(mask: np.ndarray):
"""Return number of runs and their lengths (consecutive True segments)."""
if mask.sum() == 0:
return 0, np.array([], dtype=int)
m = mask.astype(bool)
dm = np.diff(np.concatenate(([False], m, [False])).astype(np.int8))
starts = np.where(dm == 1)[0]
ends = np.where(dm == -1)[0]
lens = ends - starts
return len(lens), lens
def report_underpredictions(
timestamps: pd.Series,
actual: np.ndarray,
pred: np.ndarray,
out_csv_path
):
"""
Save a per-minute CSV of under-predictions (pred < actual) and
return a dict of summary metrics.
"""
actual = actual.astype("float64")
pred = pred.astype("float64")
finite = np.isfinite(actual) & np.isfinite(pred)
if finite.sum() == 0:
return {
"n_pred_points": 0, "n_under": 0, "under_rate": np.nan,
"mae_under": np.nan, "sum_under": np.nan, "max_under": np.nan,
"p95_under": np.nan, "n_runs": 0, "avg_run": np.nan, "max_run": 0
}
under = finite & (pred < actual)
gap = (actual - pred) # positive when under
rel_gap = np.divide(gap, np.maximum(actual, 1e-9))
# summary metrics
n_pred = int(finite.sum())
n_under = int(under.sum())
under_rate = n_under / n_pred
if n_under > 0:
mae_under = float(gap[under].mean())
sum_under = float(gap[under].sum())
max_under = float(gap[under].max())
p95_under = float(np.percentile(gap[under], 95))
runs, lens = _run_lengths(under)
avg_run = float(lens.mean()) if runs > 0 else np.nan
max_run = int(lens.max()) if runs > 0 else 0
else:
mae_under = sum_under = max_under = p95_under = np.nan
runs = 0; avg_run = np.nan; max_run = 0
# write detailed CSV (only the under-predicted rows)
df = pd.DataFrame({
"timestamp": np.asarray(timestamps)[finite],
"actual": actual[finite],
"pred": pred[finite],
"under": (pred[finite] < actual[finite]),
"gap": gap[finite],
"rel_gap": rel_gap[finite],
})
df_under = df[df["under"]].drop(columns=["under"])
df_under.to_csv(out_csv_path, index=False)
return {
"n_pred_points": n_pred,
"n_under": n_under,
"under_rate": under_rate,
"mae_under": mae_under,
"sum_under": sum_under,
"max_under": max_under,
"p95_under": p95_under,
"n_runs": runs,
"avg_run": avg_run,
"max_run": max_run,
}
# ----------------------------
# Config
# ----------------------------
# NOTE: fixed missing comma in DATE_STRS
DATE_STRS = ["20250828", "20250829"] # train/eval days handled independently
LOG_DIR = "artifacts"
RESULT_DIR = Path("results")
SEQ_LEN = 45 # history length (minutes)
PRED_LEN = 10 # forecast horizon (minutes)
BATCH = 256
EPOCHS_FIRST = 4 # per-target epochs on the first day (you can raise)
EPOCHS_NEXT = 2 # per-target epochs on subsequent days
LR = 1e-3
ADD_TIME_OF_DAY = True # add sin/cos time-of-day features
# === Feature selection ===
ENABLE_FEATURE_SELECTION = True
AUDIT_DAY = DATE_STRS[0]
FS_EPOCHS = max(2, min(3, EPOCHS_FIRST)) # quick audit training
FS_IMPROVE_THRESH = 0.01 # drop if masking improves val loss by >=1%
FS_NOISE_BAND = 0.002 # ignore micro-changes (<0.2%)
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# Model targets
TARGETS = ["num_ues", "traffic_load"]
# Columns we aim to have per minute (some may be zero if not present)
FOCUS_COLS = [
"num_ues","traffic_load","dl_prbs","ul_prbs","dl_sched_rbs","ul_sched_rbs",
"wb_cqi","dl_mcs1","ul_mcs1","pusch_snr","pucch_snr",
"dl_bler","ul_bler","bsr","phr",
"rrc_total","rrc_unique_rnti","rrc_setup_cpl","rrc_release",
"rrc_recfg","rrc_recfg_cpl","rrc_resume","rrc_paging"
]
# ----------------------------
# Regex / Parsing
# ----------------------------
LOG_PATTERN_FULL = re.compile(
r"""\[
(?P<ts>.*?)\]
\s+UE\s+RNTI=(?P<rnti>\d+)
\s+DL_TBS=(?P<dl_tbs>\d+)
\s+UL_TBS=(?P<ul_tbs>\d+)
\s+DL_SDUs=(?P<dl_sdus>\d+)
\s+UL_SDUs=(?P<ul_sdus>\d+)
\s+DL_sched_RBs=(?P<dl_sched_rbs>\d+)
\s+UL_sched_RBs=(?P<ul_sched_rbs>\d+)
\s+DL_PRBs=(?P<dl_prbs>\d+)
\s+UL_PRBs=(?P<ul_prbs>\d+)
\s+DL_BLER=(?P<dl_bler>[\d.]+)
\s+UL_BLER=(?P<ul_bler>[\d.]+)
\s+PUSCH_SNR=(?P<pusch_snr>[\d.]+)
\s+PUCCH_SNR=(?P<pucch_snr>[\d.]+)
\s+WB_CQI=(?P<wb_cqi>\d+)
\s+DL_MCS1=(?P<dl_mcs1>\d+)
\s+UL_MCS1=(?P<ul_mcs1>\d+)
\s+DL_HARQ=(?P<dl_harq>\d+)
\s+UL_HARQ=(?P<ul_harq>\d+)
\s+BSR=(?P<bsr>\d+)
\s+PHR=(?P<phr>\d+)
""", re.VERBOSE,
)
RRC_PATTERNS: List[re.Pattern] = [
re.compile(r"\[(?P<ts>[^]]+)\].*RNTI=(?P<rnti>[0-9a-fA-F]+).*RRC[_ ]?MSG=(?P<msg>[A-Za-z_]+)", re.IGNORECASE),
re.compile(r"\[(?P<ts>[^]]+)\].*rrc[=:](?P<msg>[A-Za-z_]+).*rnti[=:](?P<rnti>[0-9a-fA-F]+)", re.IGNORECASE),
re.compile(r"\[(?P<ts>[^]]+)\].*(?P<msg>RRC[A-Za-z]+).*RNTI=(?P<rnti>[0-9a-fA-F]+)", re.IGNORECASE),
re.compile(r"\|\s*(?P<ts>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z?).*?RNTI\s*(?P<rnti>[0-9a-fA-F]+)", re.IGNORECASE),
]
# ----------------------------
# STREAMING: Per-minute aggregators without raw-frame concatenation
# ----------------------------
C_RNTI_MIN, C_RNTI_MAX = 0x003D, 0xFFFC
@dataclass
class _MACAgg:
dl_sdus_sum: float = 0.0; ul_sdus_sum: float = 0.0
dl_sched_rbs_sum: float = 0.0; ul_sched_rbs_sum: float = 0.0
dl_prbs_sum: float = 0.0; ul_prbs_sum: float = 0.0
dl_bler_sum: float = 0.0; ul_bler_sum: float = 0.0
pusch_snr_sum: float = 0.0; pucch_snr_sum: float = 0.0
wb_cqi_sum: float = 0.0; dl_mcs1_sum: float = 0.0; ul_mcs1_sum: float = 0.0
dl_harq_sum: float = 0.0; ul_harq_sum: float = 0.0
bsr_sum: float = 0.0; phr_sum: float = 0.0
count: int = 0
dl_bytes: int = 0; ul_bytes: int = 0
ues: Optional[set] = None
def _floor_min(ts: datetime) -> pd.Timestamp:
return pd.Timestamp(ts).floor("min")
def stream_mac_minutes(files: List[str]) -> pd.DataFrame:
if not files: return pd.DataFrame()
acc: Dict[pd.Timestamp, _MACAgg] = {}
for path in files:
with open(path, "r", errors="ignore") as f:
for line in f:
m = LOG_PATTERN_FULL.search(line)
if not m: continue
d = m.groupdict()
try:
ts = datetime.fromisoformat(d["ts"]) # may raise
except Exception:
continue
t = _floor_min(ts)
A = acc.get(t)
if A is None:
A = _MACAgg(ues=set())
acc[t] = A
# unique UE when real RNTI with any traffic
try:
rnti_val = int(d["rnti"]) # MAC logs use decimal here
except Exception:
rnti_val = -1
dl_tbs = int(d["dl_tbs"]); ul_tbs = int(d["ul_tbs"])
if (C_RNTI_MIN <= rnti_val <= C_RNTI_MAX) and (dl_tbs > 0 or ul_tbs > 0):
A.ues.add(rnti_val)
# sums & bytes
A.dl_bytes += dl_tbs; A.ul_bytes += ul_tbs
A.dl_sdus_sum += int(d["dl_sdus"]); A.ul_sdus_sum += int(d["ul_sdus"])
A.dl_sched_rbs_sum += int(d["dl_sched_rbs"]); A.ul_sched_rbs_sum += int(d["ul_sched_rbs"])
A.dl_prbs_sum += int(d["dl_prbs"]); A.ul_prbs_sum += int(d["ul_prbs"])
A.dl_bler_sum += float(d["dl_bler"]); A.ul_bler_sum += float(d["ul_bler"])
A.pusch_snr_sum += float(d["pusch_snr"]); A.pucch_snr_sum += float(d["pucch_snr"])
A.wb_cqi_sum += int(d["wb_cqi"]); A.dl_mcs1_sum += int(d["dl_mcs1"]); A.ul_mcs1_sum += int(d["ul_mcs1"])
A.dl_harq_sum += int(d["dl_harq"]); A.ul_harq_sum += int(d["ul_harq"])
A.bsr_sum += int(d["bsr"]); A.phr_sum += int(d["phr"])
A.count += 1
if not acc: return pd.DataFrame()
rows = []
for t in sorted(acc.keys()):
A = acc[t]
c = max(1, A.count)
row = {
"timestamp": t,
"num_ues": len(A.ues) if A.ues is not None else 0,
"dl_bytes": A.dl_bytes,
"ul_bytes": A.ul_bytes,
"dl_sdus": A.dl_sdus_sum / c,
"ul_sdus": A.ul_sdus_sum / c,
"dl_sched_rbs": A.dl_sched_rbs_sum / c,
"ul_sched_rbs": A.ul_sched_rbs_sum / c,
"dl_prbs": A.dl_prbs_sum / c,
"ul_prbs": A.ul_prbs_sum / c,
"dl_bler": A.dl_bler_sum / c,
"ul_bler": A.ul_bler_sum / c,
"pusch_snr": A.pusch_snr_sum / c,
"pucch_snr": A.pucch_snr_sum / c,
"wb_cqi": A.wb_cqi_sum / c,
"dl_mcs1": A.dl_mcs1_sum / c,
"ul_mcs1": A.ul_mcs1_sum / c,
"dl_harq": A.dl_harq_sum / c,
"ul_harq": A.ul_harq_sum / c,
"bsr": A.bsr_sum / c,
"phr": A.phr_sum / c,
"traffic_load": A.dl_bytes + A.ul_bytes,
"mac_present": True,
}
rows.append(row)
out = pd.DataFrame(rows)
out.sort_values("timestamp", inplace=True)
return out
RRC_FAM_COLS = ["rrc_conn_req","rrc_setup_cpl","rrc_recfg","rrc_recfg_cpl","rrc_reestab",
"rrc_release","rrc_resume","rrc_suspend","rrc_paging","rrc_other"]
def canonical_rrc_family(msg: str | None) -> str:
if not msg: return "rrc_other"
m = msg.lower()
if "connectionrequest" in m or "connrequest" in m or "setupreq" in m: return "rrc_conn_req"
if "connectionsetupcomplete" in m or "setupcomplete" in m: return "rrc_setup_cpl"
if "reconfigurationcomplete" in m or "reconfigcomplete" in m: return "rrc_recfg_cpl"
if "reconfiguration" in m or "reconfig" in m: return "rrc_recfg"
if "reestablish" in m: return "rrc_reestab"
if "release" in m: return "rrc_release"
if "pag" in m: return "rrc_paging"
if "resume" in m: return "rrc_resume"
if "suspend" in m: return "rrc_suspend"
return "rrc_other"
def stream_rrc_minutes(files: List[str]) -> pd.DataFrame:
if not files: return pd.DataFrame()
total = defaultdict(int)
unique = defaultdict(set)
fam = {c: defaultdict(int) for c in RRC_FAM_COLS}
for path in files:
with open(path, "r", errors="ignore") as f:
for line in f:
ts = rnti = msg = None
for pat in RRC_PATTERNS:
m = pat.search(line)
if m:
gd = m.groupdict(); ts = gd.get("ts"); rnti = gd.get("rnti"); msg = gd.get("msg"); break
if not ts: continue
ts = ts.rstrip("Z")
try:
t = _floor_min(datetime.fromisoformat(ts))
except ValueError:
continue
total[t] += 1
fam_key = canonical_rrc_family(msg)
fam[fam_key][t] += 1
# unique per minute if we can parse rnti
rid = None
if rnti is not None:
try:
rid = int(rnti, 16) if any(c.isalpha() for c in rnti) else int(rnti, 10)
except ValueError:
pass
if rid is not None:
unique[t].add(rid)
if not total: return pd.DataFrame()
minutes = sorted(total.keys())
data = {
"timestamp": minutes,
"rrc_total": [total[t] for t in minutes],
"rrc_unique_rnti": [len(unique[t]) if t in unique else 0 for t in minutes],
"rrc_present": [True] * len(minutes),
}
for c in RRC_FAM_COLS:
data[c] = [fam[c][t] if t in fam[c] else 0 for t in minutes]
out = pd.DataFrame(data)
return out
# ----------------------------
# Per-minute features (compat helpers kept for reference)
# ----------------------------
def full_minute_index(start, end):
return pd.date_range(start.floor("min"), end.ceil("min"), freq="1min")
def add_time_of_day_feats(df: pd.DataFrame) -> pd.DataFrame:
if not ADD_TIME_OF_DAY: return df
out = df.copy()
m = out["timestamp"].dt.hour * 60 + out["timestamp"].dt.minute
out["tod_sin"] = np.sin(2*np.pi*m/1440.0).astype("float32")
out["tod_cos"] = np.cos(2*np.pi*m/1440.0).astype("float32")
return out
# ----------------------------
# STREAMING: Memory-safe build_day_features
# ----------------------------
def build_day_features(day: str) -> pd.DataFrame:
mac_files = glob.glob(os.path.join(LOG_DIR, f"mac_stats_{day}.log")) \
+ glob.glob(os.path.join(LOG_DIR, f"mac_stats_{day}_*.log"))
rrc_files = glob.glob(os.path.join(LOG_DIR, f"rrc_messages_{day}.log")) \
+ glob.glob(os.path.join(LOG_DIR, f"rrc_messages_{day}_*.log"))
mac_feat = stream_mac_minutes(mac_files)
rrc_feat = stream_rrc_minutes(rrc_files)
if mac_feat.empty and rrc_feat.empty:
raise SystemExit(f"[{day}] no parsable lines found")
# build the full per-minute index from whichever side exists
frames = [df for df in (mac_feat, rrc_feat) if not df.empty]
t_start = min(df["timestamp"].min() for df in frames)
t_end = max(df["timestamp"].max() for df in frames)
idx = pd.date_range(t_start.floor("min"), t_end.ceil("min"), freq="1min")
# --- MAC side: reindex or fabricate skeleton if empty
if not mac_feat.empty:
mac_feat = (mac_feat.set_index("timestamp")
.reindex(idx).fillna(0.0)
.rename_axis("timestamp").reset_index())
mac_feat["mac_present"] = True
else:
mac_feat = pd.DataFrame({"timestamp": idx})
mac_feat["mac_present"] = False # no MAC rows this minute
# --- RRC side: reindex or fabricate skeleton if empty
if not rrc_feat.empty:
rrc_feat = (rrc_feat.set_index("timestamp")
.reindex(idx).fillna(0.0)
.rename_axis("timestamp").reset_index())
rrc_feat["rrc_present"] = True
else:
rrc_feat = pd.DataFrame({"timestamp": idx})
# minimal set the rest of the code expects:
rrc_feat["rrc_total"] = 0.0
rrc_feat["rrc_unique_rnti"] = 0.0
for c in RRC_FAM_COLS:
rrc_feat[c] = 0.0
rrc_feat["rrc_present"] = False
# merge & flags
df = (mac_feat.merge(rrc_feat, on="timestamp", how="left")
.fillna(0.0).reset_index(drop=True))
present_cols = []
if "mac_present" in df: present_cols.append(df["mac_present"].astype(bool))
if "rrc_present" in df: present_cols.append(df["rrc_present"].astype(bool))
df["observed"] = False if not present_cols else np.logical_or.reduce(present_cols)
# time-of-day & compact dtypes
if ADD_TIME_OF_DAY:
m = df["timestamp"].dt.hour * 60 + df["timestamp"].dt.minute
df["tod_sin"] = np.sin(2*np.pi*m/1440.0).astype("float32")
df["tod_cos"] = np.cos(2*np.pi*m/1440.0).astype("float32")
float64_cols = df.select_dtypes(include=["float64"]).columns
if len(float64_cols): df[float64_cols] = df[float64_cols].astype("float32")
return df
# ----------------------------
# Sequence dataset (streaming)
# ----------------------------
def valid_t_indices(observed: np.ndarray, seq_len: int, pred_len: int) -> np.ndarray:
"""Return t indices where [t-seq_len, t+pred_len-1] are all observed=True."""
obs = observed.astype(np.uint8)
w = seq_len + pred_len
s = pd.Series(obs).rolling(w, min_periods=w).sum().to_numpy()
idx = []
for i in range(w - 1, len(obs)):
if s[i] == w:
t = i - pred_len + 1
if t >= seq_len:
idx.append(t)
return np.array(idx, dtype=np.int64)
class TSWindowDataset(Dataset):
def __init__(self, values_norm: np.ndarray, t_indices: np.ndarray, seq_len: int, pred_len: int, tgt_idx: int):
self.V = values_norm
self.t_idx = t_indices
self.seq = seq_len
self.h = pred_len
self.tgt = tgt_idx
def __len__(self): return len(self.t_idx)
def __getitem__(self, i):
t = self.t_idx[i]
x = self.V[t - self.seq : t, :]
y = self.V[t : t + self.h, self.tgt]
return torch.from_numpy(x), torch.from_numpy(y)
# ----------------------------
# LSTM model
# ----------------------------
class Seq2PointLSTM(nn.Module):
def __init__(self, input_size, hidden=128, layers=1, pred_len=PRED_LEN, dropout=0.0):
super().__init__()
self.lstm = nn.LSTM(input_size, hidden, layers, batch_first=True, dropout=dropout if layers>1 else 0.0)
self.fc = nn.Linear(hidden, pred_len)
def forward(self, x):
enc, _ = self.lstm(x)
return self.fc(enc[:, -1, :])
# ----------------------------
# Train / Forecast per day
# ----------------------------
def prepare_day_matrix(day_df: pd.DataFrame, feat_cols_override: List[str] | None = None) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, list]:
"""Return values_norm, mu, sigma, observed, feature_names in a fixed order."""
base_cols = FOCUS_COLS + (["tod_sin","tod_cos"] if ADD_TIME_OF_DAY else [])
feat_cols = feat_cols_override if feat_cols_override is not None else base_cols
# ensure presence; if missing, create zeros
df = day_df.copy()
for c in feat_cols:
if c not in df.columns:
df[c] = 0.0
Xdf = df[feat_cols].astype("float32").copy()
observed = df["observed"].to_numpy()
obs_mask = observed.astype(bool)
mu = Xdf[obs_mask].mean(0).to_numpy(dtype=np.float32)
sigma = Xdf[obs_mask].std(0).to_numpy(dtype=np.float32) + 1e-8
values_norm = ((Xdf.to_numpy(dtype=np.float32) - mu) / sigma).astype(np.float32)
return values_norm, mu, sigma, observed.astype(np.bool_), feat_cols
def build_dataloaders(values_norm: np.ndarray, observed: np.ndarray, feat_cols: list, target: str):
tgt_idx = feat_cols.index(target)
t_idx_all = valid_t_indices(observed, SEQ_LEN, PRED_LEN)
if len(t_idx_all) < 100:
raise RuntimeError(f"Not enough valid windows for {target} on this day.")
split = int(0.9 * len(t_idx_all))
t_tr, t_va = t_idx_all[:split], t_idx_all[split:]
ds_tr = TSWindowDataset(values_norm, t_tr, SEQ_LEN, PRED_LEN, tgt_idx)
ds_va = TSWindowDataset(values_norm, t_va, SEQ_LEN, PRED_LEN, tgt_idx)
dl_tr = DataLoader(ds_tr, batch_size=BATCH, shuffle=True, num_workers=0, pin_memory=True)
dl_va = DataLoader(ds_va, batch_size=BATCH, shuffle=False, num_workers=0, pin_memory=True)
return dl_tr, dl_va, tgt_idx
def eval_val_loss(model: nn.Module, dl_va: DataLoader) -> float:
crit = nn.MSELoss()
was_training = model.training # remember mode
model.eval()
with torch.no_grad():
tot = 0.0; n = 0
for xb, yb in dl_va:
xb = xb.to(DEVICE); yb = yb.to(DEVICE)
pred = model(xb)
tot += crit(pred, yb).item() * xb.size(0); n += xb.size(0)
if was_training:
model.train(True) # restore mode
return tot / max(1, n)
def train_one_day(model: nn.Module, dl_tr: DataLoader, dl_va: DataLoader, epochs: int) -> float:
opt = torch.optim.Adam(model.parameters(), lr=LR)
crit = nn.MSELoss()
best_va = float("inf")
for _ in range(epochs):
model.train(True)
for xb, yb in dl_tr:
xb = xb.to(DEVICE); yb = yb.to(DEVICE)
opt.zero_grad()
pred = model(xb)
loss = crit(pred, yb)
loss.backward()
opt.step()
va = eval_val_loss(model, dl_va)
best_va = min(best_va, va)
return best_va
def rolling_forecast_day(model: nn.Module, values_norm: np.ndarray, observed: np.ndarray,
feat_cols: list, target: str, mu: np.ndarray, sigma: np.ndarray) -> np.ndarray:
tgt_idx = feat_cols.index(target)
N = values_norm.shape[0]
merged = np.full(N, np.nan, dtype=np.float32)
counts = np.zeros(N, dtype=np.float32)
t_idx_all = valid_t_indices(observed, SEQ_LEN, PRED_LEN)
model.eval()
with torch.no_grad():
for t in t_idx_all:
x = torch.from_numpy(values_norm[t-SEQ_LEN:t, :]).unsqueeze(0).to(DEVICE)
yhat = model(x).squeeze(0).cpu().numpy() # normalized
yhat_dn = yhat * sigma[tgt_idx] + mu[tgt_idx]
for j, v in enumerate(yhat_dn):
i = t + j
if i < N:
if np.isnan(merged[i]): merged[i] = 0.0
merged[i] += v; counts[i] += 1.0
with np.errstate(invalid="ignore", divide="ignore"):
merged = np.divide(merged, counts, out=merged, where=counts > 0)
return merged
# ----------------------------
# ZERO-COPY: Feature selection (mask-on-the-fly)
# ----------------------------
def eval_val_loss_masked(model: nn.Module, dl_va: DataLoader, col_to_zero: Optional[int]) -> float:
crit = nn.MSELoss()
was_training = model.training
model.eval()
tot = 0.0; n = 0
with torch.no_grad():
for xb, yb in dl_va:
if col_to_zero is not None:
xb = xb.clone()
xb[:, :, col_to_zero] = 0.0
xb = xb.to(DEVICE); yb = yb.to(DEVICE)
pred = model(xb)
tot += crit(pred, yb).item() * xb.size(0); n += xb.size(0)
if was_training:
model.train(True)
return tot / max(1, n)
def feature_audit_and_select(day_df: pd.DataFrame, target: str, base_cols: List[str]) -> Tuple[List[str], Dict[str, float]]:
"""
Memory-safe audit:
- Train once on AUDIT_DAY (fast)
- For each feature, evaluate validation while zeroing that column on-the-fly
(no full matrix copies)
Returns (kept_features, deltas) where deltas[c] = (masked - base)/base.
"""
print(f"[{AUDIT_DAY}] auditing features for target={target}...")
values_norm, mu, sigma, observed, feat_cols = prepare_day_matrix(day_df, feat_cols_override=base_cols)
dl_tr, dl_va, _ = build_dataloaders(values_norm, observed, feat_cols, target)
# Baseline model
model = Seq2PointLSTM(input_size=len(feat_cols), hidden=128, layers=1, pred_len=PRED_LEN).to(DEVICE)
base_va = train_one_day(model, dl_tr, dl_va, epochs=FS_EPOCHS)
print(f"[{AUDIT_DAY}] baseline val MSE (audit) ~ {base_va:.6f}")
deltas: Dict[str, float] = {}
for i, col in enumerate(feat_cols):
if col == target:
deltas[col] = 0.0
continue
va_mask = eval_val_loss_masked(model, dl_va, i)
delta = (va_mask - base_va) / max(1e-12, base_va)
deltas[col] = float(delta)
harmful = [c for c, d in deltas.items() if d <= -FS_IMPROVE_THRESH and c != target]
ignored = [c for c, d in deltas.items() if abs(d) < FS_NOISE_BAND and c != target]
if harmful:
print(f"[{AUDIT_DAY}] dropping harmful features (improve ≥{FS_IMPROVE_THRESH*100:.1f}%): {harmful}")
if ignored:
print(f"[{AUDIT_DAY}] (near-zero impact features): {ignored}")
kept = [c for c in feat_cols if (c == target or c not in harmful)]
print(f"[{AUDIT_DAY}] kept {len(kept)}/{len(feat_cols)} features")
top_help = sorted([(c, d) for c, d in deltas.items() if d < 0], key=lambda x: x[1])[:8]
top_hurt = sorted([(c, d) for c, d in deltas.items() if d > 0], key=lambda x: x[1], reverse=True)[:8]
def fmt(entries): return ", ".join([f"{c}:{d*100:+.1f}%" for c,d in entries]) if entries else "-"
print(f"[{AUDIT_DAY}] best removals (↓val): {fmt(top_help)}")
print(f"[{AUDIT_DAY}] worst removals (↑val): {fmt(top_hurt)}")
return kept, deltas
# ----------------------------
# Orchestration
# ----------------------------
def run_pipeline_for_target(target: str, feat_cols: List[str], run_tag: str, day_tables: Dict[str, pd.DataFrame]) -> Dict[str, float]:
"""
Train across days (inheriting weights). Save plots & underpred CSVs.
Return dict of per-day best validation losses.
"""
print(f"\n=== [{run_tag}] Target: {target} with {len(feat_cols)} features ===")
run_dir = ensure_dir(RESULT_DIR / run_tag)
val_losses: Dict[str, float] = {}
model = None
for k, day in enumerate(DATE_STRS):
print(f"[{day}] preparing data...")
df_day = day_tables[day]
values_norm, mu, sigma, observed, used_cols = prepare_day_matrix(df_day, feat_cols_override=feat_cols)
input_size = values_norm.shape[1]
if model is None:
model = Seq2PointLSTM(input_size=input_size, hidden=128, layers=1, pred_len=PRED_LEN).to(DEVICE)
dl_tr, dl_va, _ = build_dataloaders(values_norm, observed, used_cols, target)
epochs = EPOCHS_FIRST if k == 0 else EPOCHS_NEXT
print(f"[{day}] training ({epochs} epochs)...")
va = train_one_day(model, dl_tr, dl_va, epochs)
val_losses[day] = va
print(f"[{day}] best val MSE ~ {va:.6f}")
print(f"[{day}] forecasting...")
pred = rolling_forecast_day(model, values_norm, observed, used_cols, target, mu, sigma)
actual = df_day[target].astype("float32").to_numpy()
# Under-prediction report
csv_path = run_dir / f"{target}_underpred_{day}.csv"
metrics = report_underpredictions(
timestamps=df_day["timestamp"],
actual=actual,
pred=pred,
out_csv_path=csv_path
)
print(
f"[{day}] {target} under-predictions: "
f"{metrics['n_under']}/{metrics['n_pred_points']} "
f"({metrics['under_rate']*100:.1f}%) | "
f"MAE_under={metrics['mae_under']:.3f}, "
f"sum_under={metrics['sum_under']:.3f}, "
f"max_under={metrics['max_under']:.3f}, "
f"p95_under={metrics['p95_under']:.3f}, "
f"runs={metrics['n_runs']}, avg_run={metrics['avg_run']:.2f} min, "
f"max_run={metrics['max_run']} min"
)
print("saved under-prediction CSV →", csv_path)
# Plot with under-prediction highlighting
plt.figure(figsize=(12,4))
plt.plot(actual, label=f"actual {target}", lw=1)
if np.isfinite(pred).any():
plt.plot(pred, label=f"forecast {target}", lw=2)
finite = np.isfinite(actual) & np.isfinite(pred)
under = finite & (pred < actual)
if under.any():
x = np.arange(len(actual))
pred_f = np.where(np.isfinite(pred), pred, actual)
plt.fill_between(x, pred_f, actual, where=under, alpha=0.15, color="red", label="under-predicted")
plt.title(f"{target} – {PRED_LEN}-min horizon (LSTM, day {day}) [{run_tag}]")
plt.xlabel("minutes (index within day)")
plt.ylabel(target); plt.grid(True); plt.legend()
outp = run_dir / f"{target}_forecast_{day}_lstm.png"
plt.tight_layout(); plt.savefig(outp); plt.close()
print("saved", outp)
return val_losses
def summarize_comparison(target: str, losses_base: Dict[str, float], losses_pruned: Dict[str, float]):
days = sorted(losses_base.keys())
print(f"\n=== Comparison (target={target}) ===")
for d in days:
b, p = losses_base[d], losses_pruned[d]
delta = (p - b) / max(1e-12, b)
print(f"{d}: base={b:.6f} | pruned={p:.6f} | Δ={delta*100:+.2f}%")
b_mean = np.mean([losses_base[d] for d in days])
p_mean = np.mean([losses_pruned[d] for d in days])
d_mean = (p_mean - b_mean) / max(1e-12, b_mean)
print(f"Avg: base={b_mean:.6f} | pruned={p_mean:.6f} | Δ={d_mean*100:+.2f}%")
def main():
ensure_dir(RESULT_DIR)
# Build per-day feature tables once (independently per day)
day_tables: Dict[str, pd.DataFrame] = {d: build_day_features(d) for d in DATE_STRS}
base_cols = FOCUS_COLS + (["tod_sin","tod_cos"] if ADD_TIME_OF_DAY else [])
for target in TARGETS:
# 1) Baseline full-feature run
losses_base = run_pipeline_for_target(target, feat_cols=base_cols, run_tag="baseline", day_tables=day_tables)
# 2) Audit and select (on AUDIT_DAY) then re-run with pruned set
if ENABLE_FEATURE_SELECTION:
kept_cols = base_cols
kept_map = {}
try:
kept_cols, deltas = feature_audit_and_select(day_tables[AUDIT_DAY], target, base_cols)
kept_map = deltas
except Exception as e:
print(f"[WARN] feature audit failed ({e}); proceeding without pruning.")
losses_pruned = run_pipeline_for_target(target, feat_cols=kept_cols, run_tag="pruned", day_tables=day_tables)
# 3) Summarize before/after
summarize_comparison(target, losses_base, losses_pruned)
# Save audit table
audit_path = ensure_dir(RESULT_DIR / "pruned") / f"{target}_feature_audit_{AUDIT_DAY}.csv"
if kept_map:
pd.DataFrame(
[{"feature": c, "delta_val_pct": 100.0 * kept_map[c], "dropped": (c not in kept_cols)} for c in kept_map]
).sort_values("delta_val_pct").to_csv(audit_path, index=False)
print("saved feature audit →", audit_path)
else:
print("[INFO] Feature selection disabled; only baseline run executed.")
if __name__ == "__main__":
main()
+1 -1
View File
@@ -278,7 +278,7 @@ check_install_usrp_uhd_driver(){
x=$((x + 1)) x=$((x + 1))
done done
$SUDO apt-get update $SUDO apt-get update
$SUDO apt-get -y install python-tk $boost_libs_ubuntu libusb-1.0-0-dev $SUDO apt-get -y install python3-tk $boost_libs_ubuntu libusb-1.0-0-dev
case "$(get_distribution_release)" in case "$(get_distribution_release)" in
"ubuntu20.04" | "ubuntu22.04") "ubuntu20.04" | "ubuntu22.04")
$SUDO apt-get -y install libuhd-dev libuhd4.5.0 uhd-host $SUDO apt-get -y install libuhd-dev libuhd4.5.0 uhd-host
+2 -2
View File
@@ -83,8 +83,8 @@
#define MAX_NUM_SLICES 1024 #define MAX_NUM_SLICES 1024
// RLC Entity // RLC Entity
#define RLC_TX_MAXSIZE 10000000 #define RLC_TX_MAXSIZE 100000000
#define RLC_RX_MAXSIZE 10000000 #define RLC_RX_MAXSIZE 100000000
#define SEND_MRW_ON 240 #define SEND_MRW_ON 240
#define MAX_ANT 8 #define MAX_ANT 8
// CBA constant // CBA constant
+4 -38
View File
@@ -1,40 +1,11 @@
#/*
# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
# * contributor license agreements. See the NOTICE file distributed with
# * this work for additional information regarding copyright ownership.
# * The OpenAirInterface Software Alliance licenses this file to You under
# * the OAI Public License, Version 1.1 (the "License"); you may not use this file
# * except in compliance with the License.
# * You may obtain a copy of the License at
# *
# * http://www.openairinterface.org/?page_id=698
# *
# * Unless required by applicable law or agreed to in writing, software
# * distributed under the License is distributed on an "AS IS" BASIS,
# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# * See the License for the specific language governing permissions and
# * limitations under the License.
# *-------------------------------------------------------------------------------
# * For more information about the OpenAirInterface (OAI) Software Alliance:
# * contact@openairinterface.org
# */
#---------------------------------------------------------------------
#
# Dockerfile for the Open-Air-Interface BUILD service
# Valid for Ubuntu 24.04
#
#---------------------------------------------------------------------
FROM ubuntu:noble AS ran-base FROM ubuntu:noble AS ran-base
ARG NEEDED_GIT_PROXY ARG NEEDED_GIT_PROXY
ARG TARGETARCH ARG TARGETARCH
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
ENV TZ=Europe/Paris ENV TZ=Asia/Tokyo
ENV BUILD_UHD_FROM_SOURCE=True ENV BUILD_UHD_FROM_SOURCE=True
ENV UHD_VERSION=4.8.0.0 ENV UHD_VERSION=4.4.0.0
#install developers pkg/repo
RUN apt-get update && \ RUN apt-get update && \
apt-get upgrade --yes && \ apt-get upgrade --yes && \
apt-get install --yes \ apt-get install --yes \
@@ -49,21 +20,16 @@ RUN apt-get update && \
bison \ bison \
flex \ flex \
m4 \ m4 \
# python3-pip for conf template generation
python3-pip \
wget && \ wget && \
# --break-system-packages allows externally managed environment in pip # --break-system-packages allows externally managed environment in pip
pip3 install --ignore-installed pyyaml --break-system-packages pip3 install --ignore-installed pyyaml --break-system-packages
# Add "Tini - A tiny but valid init for containers", https://github.com/krallin/tini
# it will be copied into target containers, to print exit numbers and handle signals properly
ENV TINI_VERSION=v0.19.0 ENV TINI_VERSION=v0.19.0
RUN wget https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-${TARGETARCH} -O /tini && chmod +x /tini RUN wget https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-${TARGETARCH} -O /tini
RUN chmod +x /tini
# In some network environments, GIT proxy is required
RUN /bin/bash -c "if [[ -v NEEDED_GIT_PROXY ]]; then git config --global http.proxy $NEEDED_GIT_PROXY; fi" RUN /bin/bash -c "if [[ -v NEEDED_GIT_PROXY ]]; then git config --global http.proxy $NEEDED_GIT_PROXY; fi"
# Copying only the needed files to create ran-base
WORKDIR /oai-ran/cmake_targets/tools WORKDIR /oai-ran/cmake_targets/tools
COPY cmake_targets/tools/build_helper \ COPY cmake_targets/tools/build_helper \
cmake_targets/tools/uhd-4.x-tdd-patch.diff \ cmake_targets/tools/uhd-4.x-tdd-patch.diff \
+56
View File
@@ -0,0 +1,56 @@
FROM ran-base:latest AS ran-build
ARG BUILD_OPTION
RUN rm -Rf /oai-ran
WORKDIR /oai-ran
COPY . .
# build all targets so final targets can be created afterwards
RUN /bin/sh oaienv && \
cd cmake_targets && \
mkdir -p log && \
./build_oai -c --ninja \
--gNB --RU --nrUE -w SIMU\
--build-lib "telnetsrv enbscope uescope nrscope" \
-w USRP -t Ethernet \
--build-e2 --cmake-opt -DKPM_VERSION=KPM_V2_03 --cmake-opt -DE2AP_VERSION=E2AP_V2 \
--noavx512 \
--build-tool-opt -k10 \
--cmake-opt -DCMAKE_C_FLAGS="-Werror" --cmake-opt -DCMAKE_CXX_FLAGS="-Werror" $BUILD_OPTION && \
echo "---- ldd on executables ----" && \
ldd ran_build/build/*softmodem* ran_build/build/nr-cuup ran_build/build/oairu && \
echo "---- ldd on shared libraries ----" && \
ldd ran_build/build/*.so
COPY flexric/etc/flexric /usr/local/etc/flexric/
COPY flexric/lib/flexric /usr/local/lib/flexric/
RUN echo "/usr/local/lib/flexric" > /etc/ld.so.conf.d/flexric.conf && ldconfig
RUN set -eux; \
apt-get update; \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
ca-certificates curl wget iproute2 iputils-ping dnsutils jq \
iperf iperf3 ffmpeg gdb\
nghttp2-client || true; \
if ! command -v h2load >/dev/null 2>&1; then \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends nghttp2 || true; \
fi; \
# Try to install sipp from apt; if unavailable, build from source
if apt-cache show sipp >/dev/null 2>&1; then \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends sipp; \
else \
echo "sipp not in apt, building from source..."; \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
build-essential cmake git libncurses5-dev libpcap-dev libssl-dev libpcre3-dev; \
git clone --depth=1 https://github.com/SIPp/sipp.git /tmp/sipp; \
cd /tmp/sipp; cmake -DENABLE_SSL=1 -DENABLE_PCAP=1 -DCMAKE_BUILD_TYPE=Release .; \
make -j"$(nproc)"; make install; strip /usr/local/bin/sipp || true; \
rm -rf /tmp/sipp; \
fi; \
rm -rf /var/lib/apt/lists/*
# Traffic runner
COPY tools/scripts/traffic_runner.sh /usr/local/bin/traffic_runner
RUN chmod +x /usr/local/bin/traffic_runner
ENTRYPOINT ["/bin/bash"]
+56
View File
@@ -0,0 +1,56 @@
FROM ran-base:latest AS ran-build
ARG BUILD_OPTION
RUN rm -Rf /oai-ran
WORKDIR /oai-ran
COPY . .
# build all targets so final targets can be created afterwards
RUN /bin/sh oaienv && \
cd cmake_targets && \
mkdir -p log && \
./build_oai -c --ninja \
--gNB --RU --nrUE -w SIMU\
--build-lib "telnetsrv enbscope uescope nrscope" \
-w USRP -t Ethernet \
--build-e2 --cmake-opt -DKPM_VERSION=KPM_V2_03 --cmake-opt -DE2AP_VERSION=E2AP_V2 \
--noavx512 \
--build-tool-opt -k10 \
--cmake-opt -DCMAKE_C_FLAGS="-Werror" --cmake-opt -DCMAKE_CXX_FLAGS="-Werror" $BUILD_OPTION && \
echo "---- ldd on executables ----" && \
ldd ran_build/build/*softmodem* ran_build/build/nr-cuup ran_build/build/oairu && \
echo "---- ldd on shared libraries ----" && \
ldd ran_build/build/*.so
RUN set -eux; \
apt-get update; \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
ca-certificates curl wget iproute2 iputils-ping dnsutils jq \
iperf iperf3 ffmpeg gdb\
nghttp2-client || true; \
if ! command -v h2load >/dev/null 2>&1; then \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends nghttp2 || true; \
fi; \
# Try to install sipp from apt; if unavailable, build from source
if apt-cache show sipp >/dev/null 2>&1; then \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends sipp; \
else \
echo "sipp not in apt, building from source..."; \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
build-essential cmake git libncurses5-dev libpcap-dev libssl-dev libpcre3-dev; \
git clone --depth=1 https://github.com/SIPp/sipp.git /tmp/sipp; \
cd /tmp/sipp; cmake -DENABLE_SSL=1 -DENABLE_PCAP=1 -DCMAKE_BUILD_TYPE=Release .; \
make -j"$(nproc)"; make install; strip /usr/local/bin/sipp || true; \
rm -rf /tmp/sipp; \
fi; \
rm -rf /var/lib/apt/lists/*
# Traffic runner
COPY tools/scripts/traffic_runner.sh /usr/local/bin/traffic_runner
RUN chmod +x /usr/local/bin/traffic_runner
COPY flexric/etc/flexric /usr/local/etc/flexric/
COPY flexric/lib/flexric /usr/local/lib/flexric/
RUN echo "/usr/local/lib/flexric" > /etc/ld.so.conf.d/flexric.conf && ldconfig
ENTRYPOINT ["/bin/bash"]
+8
View File
@@ -0,0 +1,8 @@
[NEAR-RIC]
NEAR_RIC_IP = 192.168.71.21
[XAPP]
DB_DIR = /tmp/
[LOG]
LEVEL = debug
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -793,10 +793,10 @@ static void pf_dl(gNB_MAC_INST *mac,
while (rbStart < rbStop && (rballoc_mask[rbStart + bwp_start] & slbitmap)) while (rbStart < rbStop && (rballoc_mask[rbStart + bwp_start] & slbitmap))
rbStart++; rbStart++;
uint16_t max_rbSize = 1; uint16_t max_rbSize = 5;
while (rbStart + max_rbSize <= rbStop && !(rballoc_mask[rbStart + max_rbSize + bwp_start] & slbitmap)) /* while (rbStart + max_rbSize <= rbStop && !(rballoc_mask[rbStart + max_rbSize + bwp_start] & slbitmap)) */
max_rbSize++; /* max_rbSize++; */
if (max_rbSize < min_rbSize) { if (max_rbSize < min_rbSize) {
LOG_D(NR_MAC, LOG_D(NR_MAC,
@@ -2173,12 +2173,16 @@ static int pf_ul(gNB_MAC_INST *nrmac,
bwp_info_t bi = get_pusch_bwp_start_size(iterator->UE); bwp_info_t bi = get_pusch_bwp_start_size(iterator->UE);
while (rbStart < bi.bwpSize && (rballoc_mask[rbStart + bi.bwpStart] & slbitmap)) while (rbStart < bi.bwpSize && (rballoc_mask[rbStart + bi.bwpStart] & slbitmap))
rbStart++; rbStart++;
/* if it's for inactivity, min_grant_prb is enough, otherwise check what /* if it's for inactivity, min_grant_prb is enough, otherwise check what
* would be the maximum */ * would be the maximum */
uint16_t max_rbSize = iterator->sched_inactive ? min_rb : bi.bwpSize; /* uint16_t max_rbSize = iterator->sched_inactive ? min_rb : bi.bwpSize; */
uint16_t max_rbSize = min_rb;
uint16_t available_rb = 1; uint16_t available_rb = 1;
while (rbStart + available_rb < bi.bwpSize && !(rballoc_mask[rbStart + bi.bwpStart + available_rb] & slbitmap) && available_rb < max_rbSize) while (rbStart + available_rb < bi.bwpSize && !(rballoc_mask[rbStart + bi.bwpStart + available_rb] & slbitmap) && available_rb < max_rbSize)
available_rb++; available_rb++;
/* LOG_I(NR_MAC, "[UE %04x] RB size: %u\n", iterator->UE->rnti, available_rb); */
if (rbStart + min_rb > bi.bwpSize || available_rb < min_rb) { if (rbStart + min_rb > bi.bwpSize || available_rb < min_rb) {
reset_beam_status(&nrmac->beam_info, frame, slot, iterator->UE->UE_beam_index, slots_per_frame, dci_beam.new_beam); reset_beam_status(&nrmac->beam_info, frame, slot, iterator->UE->UE_beam_index, slots_per_frame, dci_beam.new_beam);
+2 -3
View File
@@ -3963,9 +3963,8 @@ int encode_cellGroupConfig(NR_CellGroupConfig_t *cellGroupConfig, uint8_t *buffe
{ {
asn_enc_rval_t enc_rval = uper_encode_to_buffer(&asn_DEF_NR_CellGroupConfig, NULL, cellGroupConfig, buffer, max_buffer_size); asn_enc_rval_t enc_rval = uper_encode_to_buffer(&asn_DEF_NR_CellGroupConfig, NULL, cellGroupConfig, buffer, max_buffer_size);
AssertFatal(enc_rval.encoded > 0 && enc_rval.encoded <= max_buffer_size * 8, AssertFatal(enc_rval.encoded > 0 && enc_rval.encoded <= max_buffer_size * 8,
"ASN1 message encoding failed (%s, %lu)!\n", "ASN1 message encoding failed (%s, %lu)! max: %d\n",
enc_rval.failed_type->name, enc_rval.failed_type->name, enc_rval.encoded, max_buffer_size);
enc_rval.encoded);
return (enc_rval.encoded + 7) / 8; return (enc_rval.encoded + 7) / 8;
} }
+2 -1
View File
@@ -1835,10 +1835,11 @@ void nr_rlc_entity_am_recv_sdu(nr_rlc_entity_t *_entity,
if (entity->sdu_rejected != 0 if (entity->sdu_rejected != 0
&& entity->t_current > entity->t_log_buffer_full + 1000) { && entity->t_current > entity->t_log_buffer_full + 1000) {
LOG_E(RLC, "%d SDU rejected, SDU buffer full\n", entity->sdu_rejected); LOG_E(RLC, "%d SDU rejected, SDU buffer full\n", entity->sdu_rejected);
/* LOG_E(RLC, "tx_size: %d t_current: %lu max: %d\n" , entity->tx_size + size, entity->t_current, entity->tx_maxsize); */
entity->sdu_rejected = 0; entity->sdu_rejected = 0;
entity->t_log_buffer_full = entity->t_current; entity->t_log_buffer_full = entity->t_current;
} }
/* LOG_E(RLC, "tx_size: %d max: %d\n" , entity->tx_size + size, entity->tx_maxsize); */
if (entity->tx_size + size > entity->tx_maxsize) { if (entity->tx_size + size > entity->tx_maxsize) {
entity->sdu_rejected++; entity->sdu_rejected++;
entity->common.stats.txpdu_dd_pkts++; entity->common.stats.txpdu_dd_pkts++;
+98
View File
@@ -0,0 +1,98 @@
#!/usr/bin/env bash
#!/bin/bash
# Continuous traffic generator for OAI UE containers (no intervals, runs virtually forever).
# Usage examples:
# ./traffic_runner.sh --profile boring
# PROFILE=bulk_download ./traffic_runner.sh
set -euo pipefail
PROFILE="${PROFILE:-}"
# Fixed server coordinates
IPERF_HOST_UDP="192.168.71.129"
IPERF_HOST_TCP="192.168.71.130"
IPERF_PORT_UDP=5201
IPERF_PORT_TCP=5202
OAITUN_IP="$(ip -o -4 addr show dev oaitun_ue1 | awk '{print $4}' | cut -d/ -f1)"
# Very long duration to approximate "forever"
LONG_T="${LONG_T:-31536000}" # 1 year in seconds
# -------------- args --------------
if [[ $# -gt 0 ]]; then
case "$1" in
--profile=*) PROFILE="${1#--profile=}"; shift ;;
--profile) PROFILE="${2-}"; shift 2 ;;
-p=*) PROFILE="${1#-p=}"; shift ;;
-p) PROFILE="${2-}"; shift 2 ;;
-*) ;; # ignore other flags
*) PROFILE="$1"; shift ;;
esac
fi
[[ -n "$PROFILE" ]] || { echo "ERROR: --profile is required" >&2; exit 2; }
iperf_tcp_dl_forever() { ( iperf -c "$IPERF_HOST_TCP" -B "$OAITUN_IP" -p "$IPERF_PORT_TCP" -R -t "$LONG_T" >/dev/null 2>&1 ); }
iperf_tcp_ul_forever() { ( iperf -c "$IPERF_HOST_TCP" -B "$OAITUN_IP" -p "$IPERF_PORT_TCP" -t "$LONG_T" >/dev/null 2>&1 ); }
# iperf_udp_dl_forever() { ( nohup iperf -u -c "$IPERF_HOST_UDP" -B "$OAITUN_IP" -p "$IPERF_PORT_UDP" -R -t "$LONG_T" >/dev/null 2>&1 ); }
iperf_udp_dl_forever() { ( iperf -u -c "$IPERF_HOST_UDP" -B "$OAITUN_IP" -p "$IPERF_PORT_UDP" -R -b $1 -t "$LONG_T" >/dev/null 2>&1 ); }
iperf_udp_ul_forever() { ( iperf -u -c "$IPERF_HOST_UDP" -B "$OAITUN_IP" -p "$IPERF_PORT_UDP" -b $1 -t "$LONG_T" >/dev/null 2>&1 ); }
# ---------------- profiles ----------------
do_boring() {
iperf_udp_dl_forever 200M
wait
# iperf -c "$IPERF_HOST" -p "$IPERF_PORT" -B "$OAITUN_IP" -u -b 25M -t "$LONG_T"
}
do_browsing() {
iperf_tcp_dl_forever
iperf_tcp_ul_forever
wait
}
do_video() {
iperf_udp_dl_forever 6M
wait
}
do_bulk_download() {
iperf -c "$IPERF_HOST" -p "$IPERF_PORT" -B "$OAITUN_IP" -R -P 8 -w 1M -t "$LONG_T"
wait
}
do_voip() {
iperf -c "$IPERF_HOST" -p "$IPERF_PORT" -B "$OAITUN_IP" -R -u -b 80K -t "$LONG_T";
iperf -c "$IPERF_HOST" -p "$IPERF_PORT" -B "$OAITUN_IP" -u -b 80K -t "$LONG_T";
wait
}
do_iot_chatty() {
iperf -c "$IPERF_HOST" -p "$IPERF_PORT" -B "$OAITUN_IP" -R -u -b 40K -t "$LONG_T";
iperf -c "$IPERF_HOST" -p "$IPERF_PORT" -B "$OAITUN_IP" -u -b 20K -t "$LONG_T";
wait
}
do_gaming() {
iperf -c "$IPERF_HOST" -p "$IPERF_PORT" -B "$OAITUN_IP" -R -u -b 120K -t "$LONG_T";
iperf -c "$IPERF_HOST" -p "$IPERF_PORT" -B "$OAITUN_IP" -u -b 10K -t "$LONG_T";
wait
}
do_idle() {
tail -f /dev/null
}
# -------------- dispatch --------------
case "$PROFILE" in
boring) do_boring;;
browsing) do_browsing;;
video) do_video;;
bulk_download) do_bulk_download;;
voip) do_voip;;
iot_chatty) do_iot_chatty;;
gaming) do_gaming;;
idle) do_idle;;
*) echo "Unknown profile: $PROFILE" >&2; exit 1;;
esac