This commit is contained in:
2025-11-18 18:36:26 +09:00
parent b30f738b01
commit 7a18e84f1b
9 changed files with 253 additions and 437 deletions
@@ -2,6 +2,13 @@ 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 =
(
{
@@ -174,6 +181,7 @@ MACRLCs = (
num_cc = 1;
tr_s_preference = "local_L1";
tr_n_preference = "local_RRC";
min_grant_prb = 10;
}
);
@@ -94,7 +94,7 @@ services:
retries: 5
oai-gnb:
image: nakaolab:oct1
image: nakaolab:oct29
container_name: oai-gnb
cap_drop:
- ALL
@@ -105,6 +105,7 @@ services:
public_net:
ipv4_address: 192.168.71.140
volumes:
# - ../../conf_files/gnb.sa.band78.273prb.rfsim.2x2.conf:/opt/oai-gnb/etc/gnb.conf
- ../../conf_files/gnb.sa.band78.106prb.rfsim.yaml:/opt/oai-gnb/etc/gnb.yaml
healthcheck:
test: /bin/bash -c "pgrep nr-softmodem"
@@ -115,7 +116,7 @@ services:
retries: 5
oai-flexric:
image: nakaolab:oct1
image: nakaolab:oct29
container_name: oai-flexric
command: ["-c", "stdbuf -o0 /oai-ran/openair2/E2AP/flexric/build/examples/ric/nearRT-RIC"]
networks:
@@ -130,8 +131,14 @@ services:
timeout: 5s
xapp-chatgpt:
image: nakaolab:oct1
command: ["-c", "XAPP_DURATION=20 stdbuf -oL /oai-ran/openair2/E2AP/flexric/build/examples/xApp/c/monitor/xapp_chatgpt"]
image: nakaolab:oct29
command: ["-c", "XAPP_DURATION=3600 /oai-ran/openair2/E2AP/flexric/build/examples/xApp/c/monitor/xapp_chatgpt"]
# command: ["-lc","ulimit -c unlimited; export XAPP_DURATION=3600; gdb -q -batch \
# -ex 'set pagination off' \
# -ex run \
# -ex 'bt full' \
# -ex 'thread apply all bt full' \
# --args /oai-ran/openair2/E2AP/flexric/build/examples/xApp/c/monitor/xapp_chatgpt"]
container_name: xapp-chatgpt
environment:
- TZ=Asia/Tokyo
@@ -147,7 +154,7 @@ services:
timeout: 5s
oai-nr-ue1:
image: nakaolab:oct1
image: nakaolab:oct29
command: ["-c", "stdbuf -oL /oai-ran/cmake_targets/ran_build/build/nr-uesoftmodem -E --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001100 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time"]
container_name: oai-nr-ue1
cap_drop:
@@ -155,8 +162,6 @@ services:
cap_add:
- NET_ADMIN # for interface bringup
- NET_RAW # for ping
# environment:
# USE_ADDITIONAL_OPTIONS: -E --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001100 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time
depends_on:
- oai-gnb
networks:
@@ -175,7 +180,7 @@ services:
retries: 5
oai-nr-ue2:
image: nakaolab:oct1
image: nakaolab:oct29
command: ["-c", "stdbuf -oL /oai-ran/cmake_targets/ran_build/build/nr-uesoftmodem -E --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001101 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time"]
container_name: oai-nr-ue2
cap_drop:
@@ -201,7 +206,7 @@ services:
retries: 5
oai-nr-ue3:
image: nakaolab:oct1
image: nakaolab:oct29
command: ["-c", "stdbuf -oL /oai-ran/cmake_targets/ran_build/build/nr-uesoftmodem -E --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001102 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time"]
container_name: oai-nr-ue3
cap_drop:
@@ -227,7 +232,7 @@ services:
retries: 5
oai-nr-ue4:
image: nakaolab:oct1
image: nakaolab:oct29
command: ["-c", "stdbuf -oL /oai-ran/cmake_targets/ran_build/build/nr-uesoftmodem -E --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001103 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time"]
container_name: oai-nr-ue4
cap_drop:
@@ -253,7 +258,7 @@ services:
retries: 5
oai-nr-ue5:
image: nakaolab:oct1
image: nakaolab:oct29
command: ["-c", "stdbuf -oL /oai-ran/cmake_targets/ran_build/build/nr-uesoftmodem -E --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001104 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time"]
container_name: oai-nr-ue5
cap_drop:
@@ -279,7 +284,7 @@ services:
retries: 5
oai-nr-ue6:
image: nakaolab:oct1
image: nakaolab:oct29
command: ["-c", "stdbuf -oL /oai-ran/cmake_targets/ran_build/build/nr-uesoftmodem -E --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001105 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time"]
container_name: oai-nr-ue6
cap_drop:
@@ -305,7 +310,7 @@ services:
retries: 5
oai-nr-ue7:
image: nakaolab:oct1
image: nakaolab:oct29
command: ["-c", "stdbuf -oL /oai-ran/cmake_targets/ran_build/build/nr-uesoftmodem -E --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001106 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time"]
container_name: oai-nr-ue7
cap_drop:
@@ -331,7 +336,7 @@ services:
retries: 5
oai-nr-ue8:
image: nakaolab:oct1
image: nakaolab:oct29
command: ["-c", "stdbuf -oL /oai-ran/cmake_targets/ran_build/build/nr-uesoftmodem -E --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001107 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time"]
container_name: oai-nr-ue8
cap_drop:
@@ -357,7 +362,7 @@ services:
retries: 5
oai-nr-ue9:
image: nakaolab:oct1
image: nakaolab:oct29
command: ["-c", "stdbuf -oL /oai-ran/cmake_targets/ran_build/build/nr-uesoftmodem -E --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001108 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time"]
container_name: oai-nr-ue9
cap_drop:
@@ -365,12 +370,6 @@ services:
cap_add:
- NET_ADMIN # for interface bringup
- NET_RAW # for ping
<<<<<<< HEAD
=======
- SYS_NICE
environment:
USE_ADDITIONAL_OPTIONS: -E --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001109 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time
>>>>>>> 2025.w42
depends_on:
- oai-gnb
networks:
@@ -389,7 +388,7 @@ services:
retries: 5
oai-nr-ue10:
image: nakaolab:oct1
image: nakaolab:oct29
command: ["-c", "stdbuf -oL /oai-ran/cmake_targets/ran_build/build/nr-uesoftmodem -E --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001109 -C 3319680000 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time"]
container_name: oai-nr-ue10
cap_drop:
@@ -411,25 +410,36 @@ services:
interval: 10s
timeout: 5s
retries: 5
# iperf:
# image: phlak/iperf
# container_name: iperf
# iperf3:
# image: networkstatic/iperf3:latest
# container_name: iperf3
# command: ["-c 'for p in $(seq 5201 5210); do iperf3 -s -p $$p & done;'"]
# ports:
# - "5201:5201/tcp"
# - "5201:5201/udp"
# - "5201-5210:5201-5210/tcp"
# - "5201-5210:5201-5210/udp"
# networks:
# traffic_net:
# ipv4_address: 192.168.72.130
# public_net:
# ipv4_address: 192.168.71.130
# restart: unless-stopped
iperf:
iperf_udp:
image: mlabbe/iperf
container_name: iperf
command: ["-s", "-p 5201"]
container_name: iperf_udp
command: ["-u -s -p 5201"]
ports:
- "5201:5201/tcp"
- "5201:5201/udp"
networks:
public_net:
ipv4_address: 192.168.71.129
restart: unless-stopped
iperf_tcp:
image: mlabbe/iperf
container_name: iperf_tcp
command: ["-s -p 5202"]
ports:
- "5202:5202/tcp"
networks:
public_net:
ipv4_address: 192.168.71.130
+133 -133
View File
@@ -1,28 +1,21 @@
#!/bin/bash
# Examples:
# ./exp-35.sh
# UE_MIN=2 UE_MAX=12 UE_PEAK_HOUR=14 UE_NOISE_FRAC=0.15 ./exp-35.sh
# TEST_UE=oai-nr-ue1 TEST_PROFILE=browsing TEST_DURATION_SEC=120 ./exp-35.sh
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 Burst Mode ]----------------------------
# ---------------------------[ 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}"
# Rapid/random system-level bursts: every few minutes trigger a short, global burst
# that nudges all UEs to start/extend a session at once. UE-side script watches
# /tmp/system_burst and reacts (see traffic_runner --profile random).
RANDOM_BURST_MODE="${RANDOM_BURST_MODE:-1}" # 1=enabled, 0=disabled
BURST_MIN_GAP_SEC="${BURST_MIN_GAP_SEC:-180}" # 3 minutes
BURST_MAX_GAP_SEC="${BURST_MAX_GAP_SEC:-600}" # 10 minutes
BURST_MARK_FILE="${BURST_MARK_FILE:-/tmp/system_burst}"
# -------------------------------[ Config ]------------------------------------
ARTIFACT_DIR="/home/nakaolab-go/OAI/research-main/artifacts/"
ARTIFACT_DIR="/media/nakaolab-go/2nd/artifacts"
mkdir -p "$ARTIFACT_DIR"
POWER_LOG_FILE="${POWER_LOG_FILE:-$ARTIFACT_DIR/power_usage.csv}"
@@ -30,19 +23,19 @@ 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
MAC_STATS_CONTAINER="${MAC_STATS_CONTAINER:-xapp-chatgpt}"
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}"
UE_SCALER_INTERVAL_SEC="${UE_SCALER_INTERVAL_SEC:-60}"
# Docker compose helper
DC_BIN=(docker compose)
dc() { command "${DC_BIN[@]}" "$@"; }
dc down -v
dc down -v --remove-orphans
# Service names (override if your compose uses different ones)
AMF_SERVICE="${AMF_SERVICE:-oai-amf}"
@@ -54,14 +47,14 @@ XAPP_SERVICE="${XAPP_SERVICE:-xapp-chatgpt}"
UE_NAME_REGEX="${UE_NAME_REGEX:-^oai-nr-ue}"
# Test controls
TEST_UE="${TEST_UE:-}" # e.g. oai-nr-ue1
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:-}" # Max UEs; default = total UEs in compose
UE_MAX=10
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
@@ -74,6 +67,7 @@ declare -A PWR_MW_PROFILE=(
[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
)
@@ -101,14 +95,8 @@ get_core_services() {
}
get_running_ues() {
# Running UEs by service name (include TEST_UE name if custom)
if [[ -n "${TEST_UE:-}" ]]; then
dc ps --services --filter "status=running" \
| grep -E "${UE_NAME_REGEX}|^${TEST_UE}$" || true
else
dc ps --services --filter "status=running" \
| grep -E "${UE_NAME_REGEX}" || true
fi
dc ps --services --filter "status=running" \
| grep -E "${UE_NAME_REGEX}" || true
}
# -------------------------[ Readiness helpers ]-------------------------------
@@ -153,7 +141,6 @@ wait_log() {
local start; start="$(ts_now)"
while :; do
if dc logs "$svc" --since=2m 2>&1 | grep -Eiq -- "$pat"; then
log "wait_log: detected '$pat' in '$svc'."
return 0
fi
if (( $(ts_now) - start > timeout )); then
@@ -174,15 +161,14 @@ get_container_ip() {
start_services_list() {
[[ $# -eq 0 ]] && return 0
dc up -d "$@"
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"
sleep 10
dc up -d "$svc" >/dev/null 2>&1
done
}
@@ -200,11 +186,28 @@ start_in_stages() {
wait_healthy "${AMF_SERVICE}" 15
wait_healthy "${UPF_SERVICE}" 15
exec_safe "${UPF_SERVICE}" 'bash -lc "
GW=192.168.72.129
DEV=\$(ip -4 route get \$GW 2>/dev/null | awk '\''/ dev /{for(i=1;i<=NF;i++) if(\$i==\"dev\"){print \$(i+1); exit}}'\''); DEV=\${DEV:-eth0}
ip -4 route replace \$GW/32 dev \$DEV
ip -4 route replace default via \$GW dev \$DEV onlink
"'
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
@@ -272,17 +275,6 @@ calc_target_ue_count() {
}'
}
wait_oaitun() {
local ue="$1" timeout="${2:-20}"
for ((i=1; i<=timeout; i++)); do
if exec_safe "$ue" 'ip link show oaitun_ue1 >/dev/null 2>&1'; then
return 0
fi
sleep 1
done
return 1
}
ensure_ue_count() {
local target="$1"
@@ -312,7 +304,12 @@ ensure_ue_count() {
if (( cur < target )); then
local need=$(( target - cur ))
log "UE scaler: need +${need} UEs (cur=${cur}, target=${target})."
# Choose candidates (prefer random if shuf available)
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)
@@ -321,16 +318,15 @@ ensure_ue_count() {
fi
if (( ${#to_start[@]} > 0 )); then
start_ue_list "${to_start[@]}"
# kick traffic + (best-effort) readiness
for ue in "${to_start[@]}"; do
if wait_oaitun "$ue" 20; then
if wait_log "$ue" "oaitun_ue1 successfully configured"; then
exec_safe "$ue" 'ip route replace default dev oaitun_ue1'
run_traffic_profile "$ue" "random" "0"
# run_traffic_profile "$ue" "$(choose_random_profile)" "0"
run_traffic_profile "$ue" "boring"
else
dc restart "$ue"
to_start+="$ue"
dc rm -fv "$ue" >/dev/null 2>&1
fi
done
done
fi
return 0
fi
@@ -346,46 +342,57 @@ ensure_ue_count() {
fi
for ue in "${to_stop[@]}"; do
log "Stopping UE ${ue}"
dc stop "$ue" || true
dc rm -f "$ue" || true
dc stop "$ue" >/dev/null 2>&1 &
done
wait
for ue in "${to_stop[@]}"; do
dc rm -fv "$ue" >/dev/null 2>&1 &
done
wait
}
# --------------------------[ UE traffic helpers ]-----------------------------
pick_profile() {
local r=$((RANDOM % 100))
if (( r < 30 )); then echo browsing # 45%
elif (( r < 70 )); then echo video # 35%
elif (( r < 90 )); then echo bulk_download # 12%
else echo iot_chatty # 8%
fi
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" duration="${3:-0}"
exec_safe "$ue" "echo 'traffic runner ready for $ue. profile: $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 '
if [ ! -f /tmp/traffic_profile.lock ]; then
prof=\$1; dur=\$2
/usr/local/bin/traffic_runner --profile \$prof --duration \$dur >/tmp/traffic_runner.log 2>&1 &
printf \"%s %s\n\" \"\$prof\" \"\$dur\" > /tmp/traffic_profile.lock
fi
' -- $profile $duration"
prof=\$1;
/tmp/traffic_runner.sh --profile \$prof &
' -- $profile"
}
# --------------------------[ RRC / MAC scraping ]-----------------------------
rotate_mac_capture() {
rotate_stats_capture() {
local tag="${1:-}"
[[ -z "$tag" ]] && tag="$(ts_tag)"
if dc ps --services --filter "status=running" | grep -q "^${MAC_STATS_CONTAINER}$"; then
log "Rotating mac_stats from ${MAC_STATS_CONTAINER}"
dc cp "${MAC_STATS_CONTAINER}:${MAC_STATS_FILE_IN_CONTAINER}" \
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
exec_safe "$MAC_STATS_CONTAINER" "> '${MAC_STATS_FILE_IN_CONTAINER}'"
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 "mac_stats container ${MAC_STATS_CONTAINER} not running; skipping rotate"
log "stats container ${STATS_CONTAINER} not running; skipping rotate"
fi
}
@@ -400,10 +407,10 @@ _since_arg_from_file() {
fi
}
dump_gnb_rrc_logs() {
dump_gnb_logs() {
local tag="$1"
dc logs "${GNB_SERVICE}" 2>&1 | grep -E "NR_RRC" \
> "${ARTIFACT_DIR}/gnb_rrc_${tag}.log" || true
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 ]---------------------------------
@@ -428,14 +435,16 @@ sample_power_once() {
while IFS= read -r ue; do
[[ -z "$ue" ]] && continue
local lock
lock="$(exec_safe "$ue" 'bash -lc "cat /tmp/traffic_profile.lock 2>/dev/null || true"')"
# Determine profile without container lock by consulting UE_PROFILE_MAP
local picked_profile picked_duration
picked_profile="$(awk "{print \$1}" <<<"$lock")"
picked_duration="$(awk "{print \$2}" <<<"$lock")"
if [[ "$ue" =~ ([0-9]+)$ ]]; then
__idx="${BASH_REMATCH[1]}"
picked_profile="${UE_PROFILE_MAP[$__idx]}"
fi
picked_duration=""
if [[ -z "$picked_profile" ]]; then
picked_profile="${TEST_PROFILE:-idle}"
if [[ "${TEST_MODE}" -eq 1 ]]; then picked_profile="${TEST_PROFILE}"; else picked_profile="idle"; fi
fi
while [[ "$picked_profile" == -* ]]; do
@@ -499,8 +508,8 @@ rotate_csv_if_any() {
finalize_and_rotate_artifacts() {
local tag; tag="$(ts_tag)"
rotate_mac_capture "$tag" || true
dump_gnb_rrc_logs "$tag" || true
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"
}
@@ -530,40 +539,40 @@ cleanup() {
while IFS= read -r ue; do
[[ -z "$ue" ]] && continue
dc stop "$ue" || true
dc rm -f "$ue" || true
dc rm -fv "$ue" >/dev/null 2>&1 &
done < <(dc ps --services | grep -E "${UE_NAME_REGEX}" || true)
}
# ---------------------------------[ Main ]------------------------------------
trigger_system_burst() {
mapfile -t running < <(get_running_ues)
if (( ${#running[@]} == 0 )); then
return 0
fi
log "Triggering system-wide burst for ${#running[@]} UE(s)…"
for ue in "${running[@]}"; do
exec_safe "$ue" "bash -lc 'date +%s > ${BURST_MARK_FILE}'" || true
done
}
random_target_ue_count() {
local total_ues="${UE_MAX:-}"
if [[ -z "$total_ues" ]]; then
total_ues="$(get_all_ue_services | wc -l | tr -d ' ')"
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 min="${UE_MIN:-1}"
if (( total_ues < min )); then total_ues="$min"; fi
local val
if command -v shuf >/dev/null 2>&1; then
val="$(shuf -i "${min}-${total_ues}" -n 1)"
else
local span=$(( total_ues - min + 1 ))
val=$(( min + (RANDOM % span) ))
local target=$((cur + delta))
if (( target < UE_MIN )); then
target="${UE_MIN}"
elif (( target > UE_MAX )); then
target="${UE_MAX}"
fi
echo "$val"
echo "$target"
}
main() {
trap on_interrupt INT TERM
@@ -572,24 +581,18 @@ main() {
# Staged bring-up with readiness checks for both normal and TEST modes
start_in_stages
# Initialize next system-burst time
if (( RANDOM_BURST_MODE )); then
local next_burst_ts
next_burst_ts="$(($(ts_now) + BURST_MIN_GAP_SEC + RANDOM % (BURST_MAX_GAP_SEC - BURST_MIN_GAP_SEC + 1)))"
fi
# Kick off traffic workload(s)
if [[ -n "${TEST_UE:-}" ]]; then
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_UE}")"
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_oaitun "$ue" 20; then
if wait_log "$ue" "oaitun_ue1 successfully configured"; then
exec_safe "$ue" "ip route replace default dev oaitun_ue1"
run_traffic_profile "$ue" "$prof"
else
@@ -599,7 +602,7 @@ main() {
fi
# Optional finite duration for TEST mode
if [[ -n "${TEST_UE:-}" && "${TEST_DURATION_SEC:-0}" -gt 0 ]]; then
if [[ "${TEST_MODE}" -eq 1 && -n "${TEST_UE:-}" && "${TEST_DURATION_SEC:-0}" -gt 0 ]]; then
RUN_STOP_TS=$(( $(ts_now) + TEST_DURATION_SEC ))
fi
@@ -612,14 +615,6 @@ main() {
log "Entering main loop (sampling + diurnal scaler)…"
while :; do
local now; now="$(ts_now)"
# System-wide random burst scheduler
if (( RANDOM_BURST_MODE )); then
if (( now >= next_burst_ts )); then
trigger_system_burst || true
next_burst_ts="$(( now + BURST_MIN_GAP_SEC + RANDOM % (BURST_MAX_GAP_SEC - BURST_MIN_GAP_SEC + 1) ))"
fi
fi
if (( now - last_power >= POWER_LOG_INTERVAL_SEC )); then
sample_power_once || true
@@ -632,9 +627,14 @@ main() {
fi
# Re-evaluate diurnal target (normal mode only)
if [[ -z "${TEST_UE:-}" ]] && (( 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 "Diurnal scaler tick: target = ${target}"
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
+1 -1
View File
@@ -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
-2
View File
@@ -20,8 +20,6 @@ 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
-66
View File
@@ -1,66 +0,0 @@
FROM ran-base:latest AS ran-build
ARG BUILD_OPTION
ARG E2AP_VERSION=E2AP_V2
ARG KPM_VERSION=KPM_V2_03
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 \
--build-lib "telnetsrv enbscope uescope nrscope" \
-w USRP -t Ethernet \
--build-e2 --cmake-opt -DKPM_VERSION=$KPM_VERSION --cmake-opt -DE2AP_VERSION=$E2AP_VERSION \
--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 rm -rf openair2/E2AP/flexric/build && \
mkdir -p openair2/E2AP/flexric/build && \
cd openair2/E2AP/flexric/build && \
cmake -DCMAKE_BUILD_TYPE=Release \
-DKPM_VERSION=KPM_V2_03 \
-DE2AP_VERSION=E2AP_V2 \
-DCMAKE_C_COMPILER=gcc-12 \
-DCMAKE_CXX_COMPILER=g++-12 .. && \
make -j8 && make install
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 \
nghttp2-client || true; \
# Ubuntu vs Debian name differences (fallback for nghttp2 client tools)
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"]
@@ -2157,8 +2157,8 @@ static int pf_ul(gNB_MAC_INST *nrmac,
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 = 10;
uint16_t max_rbSize = iterator->sched_inactive ? min_rb : bi.bwpSize;
/* uint16_t max_rbSize = 10; */
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++;
+2 -1
View File
@@ -1835,10 +1835,11 @@ void nr_rlc_entity_am_recv_sdu(nr_rlc_entity_t *_entity,
if (entity->sdu_rejected != 0
&& 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++;
Regular → Executable
+62 -197
View File
@@ -1,233 +1,98 @@
#!/usr/bin/env bash
#!/bin/bash
# Iperf-exclusive traffic generator for OAI UE containers.
# Profiles: browsing, video, bulk_download, voip, iot_chatty, gaming, idle
# Usage:
# traffic_runner --profile browsing --duration 0 --iperf-server 10.0.0.1:5201
#
# 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
# Clear exp.sh lock on exit so sessions can restart later
trap 'rm -f /tmp/traffic_profile.lock' EXIT
PROFILE="${PROFILE:-}"
DEFAULT_IPERF_PORT="${DEFAULT_IPERF_PORT:-5201}"
TRAFFIC_NET_GATEWAY="${TRAFFIC_NET_GATEWAY:-192.168.71.130}"
# 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)"
if [[ -z "${IPERF_SERVER:-}" ]]; then
# Candidate hostnames (Docker DNS on the same network) then the gateway IP
IPERF_HOST_CANDIDATES=${IPERF_HOST_CANDIDATES:-"iperf iperf3 iperf-server ${TRAFFIC_NET_GATEWAY}"}
for h in $IPERF_HOST_CANDIDATES; do
if getent hosts "$h" >/dev/null 2>&1; then
IPERF_SERVER="${h}:${DEFAULT_IPERF_PORT}"
break
fi
# Accept numeric IPv4 even if DNS not present
if [[ "$h" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
IPERF_SERVER="${h}:${DEFAULT_IPERF_PORT}"
break
fi
done
# 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
# Final check
[[ -n "${IPERF_SERVER:-}" ]] || die "No iperf server found. Set --iperf-server or connect a server to rfsim5g-oai-traffic-net."
IPERF_HOST="${IPERF_SERVER%:*}"
IPERF_PORT="${IPERF_SERVER#*:}"
[[ "$IPERF_PORT" =~ ^[0-9]+$ ]] || die "Invalid iperf port in $IPERF_SERVER"
[[ -n "$PROFILE" ]] || { echo "ERROR: --profile is required" >&2; exit 2; }
PROFILE=""
DURATION=0
LOG_FILE="/tmp/traffic_runner.log"
iperf_tcp_dl_forever() { ( nohup iperf -c "$IPERF_HOST_TCP" -B "$OAITUN_IP" -p "$IPERF_PORT_TCP" -R -t "$LONG_T" >/dev/null 2>&1 & ); }
iperf_tcp_ul_forever() { ( nohup 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 -b $1 -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"; }
iperf_udp_ul_forever() { ( nohup iperf -u -c "$IPERF_HOST_UDP" -B "$OAITUN_IP" -p "$IPERF_PORT_UDP" -b $1 -t "$LONG_T" >/dev/null 2>&1 & ); }
log() { printf '[%(%Y-%m-%dT%H:%M:%S%z)T] %s\n' -1 "$*" | tee -a "$LOG_FILE" >&2; }
die() { log "ERROR: $*"; exit 2; }
# Parse args
while [[ $# -gt 0 ]]; do
case "$1" in
--profile) PROFILE="$2"; shift 2;;
--duration) DURATION="$2"; shift 2;;
--iperf-server) IPERF_SERVER="$2"; shift 2;;
--help|-h) cat <<EOF
Usage: $0 --profile <name> [--duration <sec>] [--iperf-server host:port]
Profiles: browsing, video, bulk_download, voip, iot_chatty, gaming, idle
If duration is 0 or omitted, run indefinitely.
EOF
exit 0;;
*) die "Unknown arg: $1";;
esac
done
[[ -n "$PROFILE" ]] || die "--profile is required"
[[ -n "$IPERF_SERVER" ]] || die "--iperf-server or IPERF_SERVER env is required (e.g., 192.168.0.2:5201)"
IPERF_HOST="${IPERF_SERVER%:*}"
IPERF_PORT="${IPERF_SERVER#*:}"
[[ "$IPERF_PORT" =~ ^[0-9]+$ ]] || die "Invalid iperf port in $IPERF_SERVER"
# Helpers
run_for_duration() {
# $1: command string to eval in a loop
# uses global DURATION
local start now elapsed
start=$(date +%s)
while :; do
now=$(date +%s)
elapsed=$(( now - start ))
if (( DURATION > 0 && elapsed >= DURATION )); then break; fi
eval "$1"
done
# ---------------- profiles ----------------
do_boring() {
iperf_udp_dl_forever 5M
wait
# iperf -c "$IPERF_HOST" -p "$IPERF_PORT" -B "$OAITUN_IP" -u -b 25M -t "$LONG_T"
}
# iperf helpers
iperf_tcp_dl() { iperf3 -c "$IPERF_HOST" -p "$IPERF_PORT" -R -t "$1" -J >/dev/null 2>&1 || true; }
iperf_tcp_ul() { iperf3 -c "$IPERF_HOST" -p "$IPERF_PORT" -t "$1" -J >/dev/null 2>&1 || true; }
iperf_udp_dl() { iperf3 -c "$IPERF_HOST" -p "$IPERF_PORT" -R -u -b "$2" -t "$1" -J >/dev/null 2>&1 || true; }
iperf_udp_ul() { iperf3 -c "$IPERF_HOST" -p "$IPERF_PORT" -u -b "$2" -t "$1" -J >/dev/null 2>&1 || true; }
# Profiles
do_browsing() {
log "Browsing (TCP bursts, short think-time) to $IPERF_SERVER"
run_for_duration '
dur=$(( 1 + RANDOM % 3 )) # 1-3s burst
iperf_tcp_dl "$dur"
sleep "0.$((100 + RANDOM % 600))" # 0.1-0.7s think
'
iperf_tcp_dl_forever
iperf_tcp_ul_forever
wait
}
do_video() {
log "Video (FHD video 5 Mbps sustained) to $IPERF_SERVER"
run_for_duration '
iperf3 -c "'"$IPERF_HOST"'" -p "'"$IPERF_PORT"'" -R -u -b 5M -t 30 -J >/dev/null 2>&1
sleep 1
'
iperf_udp_dl_forever 6M
wait
}
do_bulk_download() {
# Heavy TCP downlink with multiple streams
log "Bulk download (TCP DL 8 parallel, long sustained) to $IPERF_SERVER"
run_for_duration '
iperf3 -c "'"$IPERF_HOST"'" -p "'"$IPERF_PORT"'" -R -P 8 -t 20 -w 1M -J >/dev/null 2>&1
sleep 1
'
iperf -c "$IPERF_HOST" -p "$IPERF_PORT" -B "$OAITUN_IP" -R -P 8 -w 1M -t "$LONG_T"
wait
}
do_voip() {
log "VoIP (UDP bi-di ~100–200 kbps) to $IPERF_SERVER"
run_for_duration '
iperf_udp_ul 5 120K &
iperf_udp_dl 5 120K &
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
sleep 0.5
'
}
do_iot_chatty() {
log "IoT chatty (UDP UL ~50–80 kbps bursts) to $IPERF_SERVER"
run_for_duration '
iperf_udp_ul 2 60K
sleep $(( 3 + RANDOM % 7 ))
'
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() {
log "Gaming (UDP bi-di ~1–3 Mbps bursts) to $IPERF_SERVER"
run_for_duration '
rate_ul=$(( 1 + RANDOM % 3 ))M
rate_dl=$(( 1 + RANDOM % 3 ))M
iperf_udp_ul 5 "$rate_ul" &
iperf_udp_dl 5 "$rate_dl" &
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
sleep 0.5
'
}
do_idle() {
log "Idle"
run_for_duration "sleep 2"
}
# ------------- Randomized pattern (bursts of 30s..minutes) ------------------
# Defaults can be overridden via env:
# MIN_ACTIVE_SEC / MAX_ACTIVE_SEC — each session duration (default 30..180)
# MIN_IDLE_SEC / MAX_IDLE_SEC — wait between sessions (default 5..60)
# BURST_FILE — path watched for system-wide bursts
# BURST_MAX_AGE_SEC — consider burst file "fresh" within N sec
MIN_ACTIVE_SEC="${MIN_ACTIVE_SEC:-30}"
MAX_ACTIVE_SEC="${MAX_ACTIVE_SEC:-180}"
MIN_IDLE_SEC="${MIN_IDLE_SEC:-5}"
MAX_IDLE_SEC="${MAX_IDLE_SEC:-60}"
BURST_FILE="${BURST_FILE:-/tmp/system_burst}"
BURST_MAX_AGE_SEC="${BURST_MAX_AGE_SEC:-45}"
_pick_random_profile() {
# Weighted pick to feel realistic; adjust as desired
local r=$((RANDOM % 100))
if (( r < 40 )); then echo browsing # 40%
elif (( r < 55 )); then echo voip # 15%
elif (( r < 70 )); then echo video # 15%
elif (( r < 85 )); then echo gaming # 15%
else echo bulk_download # 15%
fi
}
_run_profile_for() {
local name="$1" seconds="$2"
DURATION="$seconds"
case "$name" in
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;;
*) do_browsing;;
esac
}
_is_burst_now() {
# return 0 if a fresh burst marker exists
[[ -f "$BURST_FILE" ]] || return 1
local now mtime age
now=$(date +%s)
mtime=$(stat -c %Y "$BURST_FILE" 2>/dev/null || echo 0)
age=$(( now - mtime ))
(( age >= 0 && age <= BURST_MAX_AGE_SEC ))
}
do_random() {
log "Random pattern: independent per-UE bursts to $IPERF_SERVER (session: ${MIN_ACTIVE_SEC}-${MAX_ACTIVE_SEC}s; idle: ${MIN_IDLE_SEC}-${MAX_IDLE_SEC}s)"
while :; do
local prof dur idle
prof="$(_pick_random_profile)"
dur=$(( MIN_ACTIVE_SEC + RANDOM % (MAX_ACTIVE_SEC - MIN_ACTIVE_SEC + 1) ))
if _is_burst_now; then
# If a system burst is signaled, start immediately and slightly extend session
idle=0
dur=$(( dur + 20 + RANDOM % 40 )) # +20..60s
else
idle=$(( MIN_IDLE_SEC + RANDOM % (MAX_IDLE_SEC - MIN_IDLE_SEC + 1) ))
fi
log "Random session: profile=${prof} duration=${dur}s (then idle ${idle}s)"
_run_profile_for "$prof" "$dur" || true
sleep "$idle"
done
tail -f /dev/null
}
# -------------- dispatch --------------
case "$PROFILE" in
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;;
random) do_random;;
*) die "Unknown profile: $PROFILE";;
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