Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02dc8dadd4 | ||
|
|
63d2238429 | ||
|
|
524e26ede5 | ||
|
|
01db9857c4 | ||
|
|
aa353fdcff | ||
|
|
bdfe516aa7 | ||
|
|
f8f10665e7 | ||
|
|
ee3e027041 | ||
|
|
7aca2a5fdf | ||
|
|
636c124596 | ||
|
|
7a18e84f1b | ||
|
|
b30f738b01 | ||
|
|
ac4dbff76e |
@@ -5,3 +5,4 @@ common/utils/T/T_IDs.h
|
||||
common/utils/T/T_messages.txt.h
|
||||
common/utils/T/genids
|
||||
common/utils/T/genids.o
|
||||
artifacts
|
||||
|
||||
Executable
+15
@@ -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/
|
||||
@@ -9,20 +9,29 @@ channelmod = {
|
||||
model_name = "rfsimu_channel_enB0"
|
||||
type = "AWGN";
|
||||
ploss_dB = 0;
|
||||
noise_power_dB = -10;
|
||||
forgetfact = 0;
|
||||
offset = 0;
|
||||
ds_tdl = 0;
|
||||
noise_power_dB = -60;
|
||||
# forgetfact = 0;
|
||||
# offset = 0;
|
||||
# ds_tdl = 0;
|
||||
},
|
||||
{
|
||||
model_name = "rfsimu_channel_ue0"
|
||||
type = "AWGN";
|
||||
ploss_dB = 0;
|
||||
noise_power_dB = 0;
|
||||
forgetfact = 0;
|
||||
offset = 0;
|
||||
ds_tdl = 0;
|
||||
}
|
||||
# ploss_dB = 0;
|
||||
# noise_power_dB = -30;
|
||||
# forgetfact = 0;
|
||||
# offset = 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 = (
|
||||
{
|
||||
|
||||
@@ -40,7 +40,6 @@ gNBs =
|
||||
|
||||
NETWORK_INTERFACES :
|
||||
{
|
||||
|
||||
GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.71.150";
|
||||
GNB_IPV4_ADDRESS_FOR_NGU = "192.168.71.150";
|
||||
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_ip_address = ({ ipv4 = "192.168.70.132"; });
|
||||
amf_ip_address = ({ ipv4 = "192.168.71.132"; });
|
||||
|
||||
NETWORK_INTERFACES :
|
||||
{
|
||||
GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.151";
|
||||
GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.151";
|
||||
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
|
||||
};
|
||||
|
||||
@@ -169,6 +169,7 @@ MACRLCs = (
|
||||
tr_n_preference = "local_RRC";
|
||||
pusch_TargetSNRx10 = 200;
|
||||
pucch_TargetSNRx10 = 200;
|
||||
min_grant_prb = 1;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -230,7 +231,7 @@ log_config : {
|
||||
};
|
||||
|
||||
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 = "/usr/local/lib/flexric/"
|
||||
};
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
Active_gNBs:
|
||||
- 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: none
|
||||
gNBs:
|
||||
@@ -139,13 +146,13 @@ gNBs:
|
||||
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: 200
|
||||
pucch_TargetSNRx10: 200
|
||||
min_grant_prb: 5
|
||||
stats_max_ue: 17
|
||||
|
||||
L1s:
|
||||
@@ -193,6 +200,6 @@ log_config:
|
||||
mac_log_level: info
|
||||
rlc_log_level: info
|
||||
pdcp_log_level: info
|
||||
rrc_log_level: info
|
||||
rrc_log_level: debug
|
||||
ngap_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 = "none";
|
||||
|
||||
e2_agent = {
|
||||
enabled = "yes";
|
||||
near_ric_ip_addr = "192.168.71.21";
|
||||
near_ric_port = 36421;
|
||||
sm_dir = "/usr/local/lib/flexric/";
|
||||
}
|
||||
|
||||
gNBs =
|
||||
(
|
||||
{
|
||||
@@ -198,7 +205,7 @@ RUs = (
|
||||
max_rxgain = 75;
|
||||
eNB_instances = [0];
|
||||
#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 :
|
||||
{
|
||||
serveraddr = "server";
|
||||
serverport = 4043;
|
||||
# serverport = 4043;
|
||||
options = (); #("saviq"); or/and "chanmod"
|
||||
modelname = "AWGN";
|
||||
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,5 +1,4 @@
|
||||
uicc0 = {
|
||||
imsi = "208990100001100";
|
||||
uicc0 = { # imsi is omitted
|
||||
key = "fec86ba6eb707ed08905757b1bb44b8f";
|
||||
opc= "C42449363BBAD02B66D16BC975D77CC1";
|
||||
dnn= "oai";
|
||||
|
||||
@@ -4,6 +4,7 @@ uicc0:
|
||||
opc: C42449363BBAD02B66D16BC975D77CC1
|
||||
dnn: oai
|
||||
nssai_sst: 1
|
||||
pdu_type: IPV4
|
||||
|
||||
thread-pool: "-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1"
|
||||
|
||||
@@ -24,7 +25,7 @@ channelmod:
|
||||
- model_name: rfsimu_channel_ue0
|
||||
type: AWGN
|
||||
ploss_dB: 20
|
||||
nose_power_dB: -2
|
||||
noise_power_dB: -2
|
||||
forgetfact: 0
|
||||
offset: 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
|
||||
+2
-35
@@ -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:
|
||||
general: debug
|
||||
@@ -103,8 +70,8 @@ amf:
|
||||
# This really depends on if we want to keep the "mini" version or not
|
||||
support_features_options:
|
||||
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.
|
||||
# There's no NRF in this scenario, SMF info is taken from "nfs" section.
|
||||
# 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.
|
||||
enable_nssf: no
|
||||
enable_smf_selection: no
|
||||
relative_capacity: 30
|
||||
+1
-11
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
|
||||
[mysqld]
|
||||
wait_timeout = 2
|
||||
interactive_timeout = 2
|
||||
Executable
+650
@@ -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()
|
||||
@@ -278,7 +278,7 @@ check_install_usrp_uhd_driver(){
|
||||
x=$((x + 1))
|
||||
done
|
||||
$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
|
||||
"ubuntu20.04" | "ubuntu22.04")
|
||||
$SUDO apt-get -y install libuhd-dev libuhd4.5.0 uhd-host
|
||||
|
||||
@@ -83,8 +83,8 @@
|
||||
#define MAX_NUM_SLICES 1024
|
||||
|
||||
// RLC Entity
|
||||
#define RLC_TX_MAXSIZE 10000000
|
||||
#define RLC_RX_MAXSIZE 10000000
|
||||
#define RLC_TX_MAXSIZE 100000000
|
||||
#define RLC_RX_MAXSIZE 100000000
|
||||
#define SEND_MRW_ON 240
|
||||
#define MAX_ANT 8
|
||||
// CBA constant
|
||||
|
||||
@@ -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
|
||||
ARG NEEDED_GIT_PROXY
|
||||
ARG TARGETARCH
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
ENV TZ=Europe/Paris
|
||||
ENV TZ=Asia/Tokyo
|
||||
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 && \
|
||||
apt-get upgrade --yes && \
|
||||
apt-get install --yes \
|
||||
@@ -49,21 +20,16 @@ RUN apt-get update && \
|
||||
bison \
|
||||
flex \
|
||||
m4 \
|
||||
# python3-pip for conf template generation
|
||||
python3-pip \
|
||||
wget && \
|
||||
# --break-system-packages allows externally managed environment in pip
|
||||
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
|
||||
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"
|
||||
|
||||
# Copying only the needed files to create ran-base
|
||||
WORKDIR /oai-ran/cmake_targets/tools
|
||||
COPY cmake_targets/tools/build_helper \
|
||||
cmake_targets/tools/uhd-4.x-tdd-patch.diff \
|
||||
|
||||
@@ -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"]
|
||||
@@ -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"]
|
||||
@@ -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.
+1
-1
Submodule openair2/E2AP/flexric updated: 6a595d8b24...cea0e5d9ed
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))
|
||||
rbStart++;
|
||||
|
||||
uint16_t max_rbSize = 1;
|
||||
uint16_t max_rbSize = 5;
|
||||
|
||||
while (rbStart + max_rbSize <= rbStop && !(rballoc_mask[rbStart + max_rbSize + bwp_start] & slbitmap))
|
||||
max_rbSize++;
|
||||
/* while (rbStart + max_rbSize <= rbStop && !(rballoc_mask[rbStart + max_rbSize + bwp_start] & slbitmap)) */
|
||||
/* max_rbSize++; */
|
||||
|
||||
if (max_rbSize < min_rbSize) {
|
||||
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);
|
||||
while (rbStart < bi.bwpSize && (rballoc_mask[rbStart + bi.bwpStart] & slbitmap))
|
||||
rbStart++;
|
||||
|
||||
/* if it's for inactivity, min_grant_prb is enough, otherwise check what
|
||||
* 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;
|
||||
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) {
|
||||
reset_beam_status(&nrmac->beam_info, frame, slot, iterator->UE->UE_beam_index, slots_per_frame, dci_beam.new_beam);
|
||||
|
||||
@@ -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);
|
||||
AssertFatal(enc_rval.encoded > 0 && enc_rval.encoded <= max_buffer_size * 8,
|
||||
"ASN1 message encoding failed (%s, %lu)!\n",
|
||||
enc_rval.failed_type->name,
|
||||
enc_rval.encoded);
|
||||
"ASN1 message encoding failed (%s, %lu)! max: %d\n",
|
||||
enc_rval.failed_type->name, enc_rval.encoded, max_buffer_size);
|
||||
return (enc_rval.encoded + 7) / 8;
|
||||
}
|
||||
|
||||
|
||||
@@ -1835,10 +1835,11 @@ void nr_rlc_entity_am_recv_sdu(nr_rlc_entity_t *_entity,
|
||||
if (entity->sdu_rejected != 0
|
||||
&& 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, "tx_size: %d t_current: %lu max: %d\n" , entity->tx_size + size, entity->t_current, entity->tx_maxsize); */
|
||||
entity->sdu_rejected = 0;
|
||||
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) {
|
||||
entity->sdu_rejected++;
|
||||
entity->common.stats.txpdu_dd_pkts++;
|
||||
|
||||
Executable
+98
@@ -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
|
||||
Reference in New Issue
Block a user