## ----setup, cache=FALSE, include=FALSE---------------------------------------- knitr::opts_chunk$set(collapse=TRUE, echo=TRUE, eval=FALSE) # workaround for pkgdown # https://github.com/r-lib/pkgdown/issues/1159#issuecomment-541356531 do.call(knitr::read_chunk, list("includes/5G-1.R")) do.call(knitr::read_chunk, list("includes/5G-2.R")) do.call(knitr::read_chunk, list("includes/5G-3.R")) ## ----1.configuration---------------------------------------------------------- # C <- 40e9 # link capacity [bps] # rho <- 0.75 # utilization # # FH_EX <- (80 + 46) * 8 / C # avg. FH interarrival time [s] # BH_bytes <- c(40, 576, 1500) # BH traffic distribution # Weights <- c(7, 4, 1) / 12 # # BH_EX <- sum(Weights * (8 * BH_bytes / C)) # avg. BH interarrival time [s] # # FH_w <- 0.5 # FH traffic ratio # BH_w <- 1 - FH_w # BH traffic ratio # FH_lambda <- FH_w * rho / FH_EX # FH avg. rate [pkts/s] # BH_lambda <- BH_w * rho / BH_EX # BH avg. rate [pkts/s] # # Tsim <- 1e5 / (FH_lambda + BH_lambda) # simulation time ## ----1.simulation------------------------------------------------------------- # library(simmer) # # set.seed(1234) # # # parametrized simulation function # # - param[["n_switch"]] is the number of switches # # - param[["fh_prio"]] is the priority level for FH traffic # # - param[["preemptive"]] is the preemptiveness flag for switches # simulate <- function(param) { # # # single FH traffic trajectory traversing n_switch elements sequentially # fh_traffic <- lapply(1:param[["n_switch"]], function(i) { # trajectory() %>% # seize(paste0("switch", i)) %>% # timeout(FH_EX) %>% # release(paste0("switch", i)) # }) %>% join() # # # list of n_switch trajectories, one per element, modeling the interfering BH traffic # bh_traffic <- lapply(1:param[["n_switch"]], function(i) { # trajectory() %>% # seize(paste0("switch", i)) %>% # timeout(function() sample(BH_bytes*8/C, size=1, prob=Weights)) %>% # release(paste0("switch", i)) # }) # # # simulation environment # env <- simmer() %>% # # generator of FH traffic # add_generator("FH_0_", fh_traffic, function() rexp(100, FH_lambda), priority=param[["fh_prio"]]) # # for (i in 1:param[["n_switch"]]) # env %>% # # n_switch resources, one per switch # add_resource(paste0("switch", i), 1, Inf, mon=FALSE, preemptive=param[["preemptive"]]) %>% # # n_switch generators of BH traffic # add_generator(paste0("BH_", i, "_"), bh_traffic[[i]], function() rexp(100, BH_lambda)) # # env %>% # run(until=Tsim) %>% # wrap() # } # # # grid of scenarios # cases <- expand.grid(n_switch = c(1, 2, 5), # fh_prio = c(0, 1), # preemptive = c(TRUE, FALSE)) # # # parallel simulation # system.time({ # envs <- parallel::mclapply(split(cases, 1:nrow(cases)), simulate, # mc.cores=nrow(cases), mc.set.seed=FALSE) # }) ## ----1.analysis--------------------------------------------------------------- # library(tidyverse) # # bp.vals <- function(x, probs=c(0.05, 0.25, 0.5, 0.75, 0.95)) { # r <- quantile(x, probs=probs, na.rm=TRUE) # names(r) <- c("ymin", "lower", "middle", "upper", "ymax") # r # } # # arrivals <- envs %>% # get_mon_arrivals() %>% # left_join(rowid_to_column(cases, "replication")) %>% # filter(!(fh_prio==0 & preemptive==TRUE)) %>% # separate(name, c("Flow", "index", "n")) %>% # mutate(total_time = end_time - start_time, # waiting_time = total_time - activity_time, # Queue = forcats::fct_recode(interaction(fh_prio, preemptive), # "without SP" = "0.FALSE", # "with SP" = "1.FALSE", # "with SP & preemption" = "1.TRUE")) # # arrivals %>% # filter(n_switch == 1) %>% # # plot # ggplot(aes(Queue, waiting_time*1e9, color=Flow)) + theme_bw() + # stat_summary(fun.data=bp.vals, geom="boxplot", position="dodge") + # theme(legend.justification=c(0, 1), legend.position=c(.02, .98)) + # labs(y = "Queueing Delay [ns]", x = element_blank()) # # arrivals %>% # filter(Flow == "FH") %>% # # plot # ggplot(aes(factor(n_switch), waiting_time*1e9, color=Queue)) + theme_bw() + # stat_summary(fun.data=bp.vals, geom="boxplot", position="dodge") + # theme(legend.justification=c(0, 1), legend.position=c(.02, .98)) + # labs(y = "Queueing Delay [ns]", x = "No. of switches") ## ----2.configuration---------------------------------------------------------- # C <- 1.25e9 # link capacity [bps] # Tg <- 1e-6 # guard time [s] # # bytes <- c(40, 576, 1500) # traffic distribution # Weights <- c(7, 4, 1) / 12 # # EX <- sum(Weights * (8 * bytes / C)) # avg. interarrival time [s] # burst <- 20 # burst length [pkts] # # ONU_lambda <- 20e6 / C / EX # avg. rate per ONU [pkts/s] # SC_lambda <- 150e6 / C / EX # avg. rate for the SC [pkts/s] # RRH_on <- 8 * 6000 / C # RRH on period [s] # RRH_period <- RRH_on * C / 720e6 # RRH total period [s] # RRH_off <- RRH_period - RRH_on # RRH off period [s] ## ----2.helpers---------------------------------------------------------------- # # helper function: round-robin-based selection of ONUs # cyclic_counter <- function(n) { # n <- as.numeric(n) # i <- 1 # function(op) { # if (!missing(op)) { # i <<- i + op # if (i > n) i <<- 1 # else if (i < 1) i <<- n # } # i # } # } # # # helper function: generator of packet sizes of n packets # gen_pkts <- function(n) sample(bytes*8/C, size=n, prob=Weights, replace=TRUE) # # # helper function: double-exponential generator of interarrival times # gen_exp <- function(lambda, burst) function() # unlist(lapply(rexp(100/burst, lambda/burst), function(i) c(i, rep(0, rpois(1, burst))))) ## ----2.simulation------------------------------------------------------------- # library(simmer) # # set.seed(1234) # # # parametrized simulation function # # - param[["scenario"]] is the scenario identifier (A=small cell, B=RRH) # # - param[["ONU_n"]] is the number of ONUs # # - param[["limit"]] is the max. transmission window # simulate <- function(param) { # # # global variables # lambda <- rep(ONU_lambda, param[["ONU_n"]]) # limit <- rep(param[["limit"]], param[["ONU_n"]]) * 8 / C # if (param[["scenario"]] == "A") { # param[["ONU_n"]] <- param[["ONU_n"]] + 1 # lambda <- c(lambda, SC_lambda) # limit <- c(limit, Inf) # tx_t <- RRH_on + Tg + Tg * 0:param[["ONU_n"]] # } else { # Tg_n <- RRH_off %/% Tg - 1 # Tg_i <- rep(1:Tg_n, length.out=param[["ONU_n"]]+1) # period_i <- rep(0:param[["ONU_n"]], each=Tg_n, length.out=param[["ONU_n"]]+1) # tx_t <- RRH_on + RRH_period * period_i + Tg * Tg_i # } # eq_pkts <- rep(list(NULL), param[["ONU_n"]]) # tx_pkts <- rep(list(NULL), param[["ONU_n"]]) # t_head <- tail(tx_t, 1) # onu_i <- cyclic_counter(param[["ONU_n"]]) # remaining <- Inf # # # DBA logic # set_next_window <- function() { # # time until the next RRH_on # if (param[["scenario"]] == "B") # remaining <- (t_head %/% RRH_period + 1) * RRH_period - t_head # # generate new pkt lengths # pkts <- get_queue_count(env, paste0("ONU", onu_i())) # eq_pkts[[onu_i()]] <<- c(eq_pkts[[onu_i()]], gen_pkts(pkts - length(eq_pkts[[onu_i()]]))) # # reserve the next transmission window # eq_cumsum <- cumsum(eq_pkts[[onu_i()]]) # n <- sum(eq_cumsum <= min(limit[[onu_i()]], remaining - Tg)) # if ((pkts && !n) || (remaining <= Tg)) { # # go past the next RRH_on # t_head <<- t_head + remaining + RRH_on + Tg # n <- sum(eq_cumsum <= min(limit[[onu_i()]], RRH_off - 2*Tg)) # } # tx_pkts[[onu_i()]] <<- head(eq_pkts[[onu_i()]], n) # eq_pkts[[onu_i()]] <<- tail(eq_pkts[[onu_i()]], -n) # tx_t[[onu_i()]] <<- t_head # t_head <<- t_head + sum(tx_pkts[[onu_i()]]) + Tg # index <<- 0 # tx_t[[onu_i(1)]] - now(env) # } # # # list of ONU_n trajectories, one per ONU # ONUs <- lapply(1:param[["ONU_n"]], function(i) { # trajectory() %>% # seize(paste0("ONU", i)) %>% # set_capacity(paste0("ONU", i), 0) %>% # seize("link") %>% # timeout(function() { # index <<- index + 1 # tx_pkts[[i]][index] # }) %>% # release("link") %>% # release(paste0("ONU", i)) # }) # # # OLT logic # OLT <- trajectory() %>% # simmer::select(function() paste0("ONU", onu_i())) %>% # set_attribute("ONU", function() onu_i()) %>% # set_capacity_selected(function() length(tx_pkts[[onu_i()]])) %>% # timeout(function() sum(tx_pkts[[onu_i()]])) %>% # timeout(set_next_window) %>% # rollback(amount=6, times=Inf) # # # RRH logic # RRH <- trajectory() %>% # seize("link") %>% # timeout(RRH_on) %>% # release("link") %>% # timeout(RRH_off) %>% # rollback(amount=4, times=Inf) # # # simulation environment # env <- simmer() %>% # # the fiber link as a resource # add_resource("link", 1, Inf) # # if (param[["scenario"]] == "B") # # RRH worker # env %>% add_generator("RRH_", RRH, at(0)) # # for (i in 1:param[["ONU_n"]]) # env %>% # # ONU_n resources, one per ONU # add_resource(paste0("ONU", i), 0, Inf) %>% # # ONU_n traffic generators, one per ONU # add_generator(paste0("ONU_", i, "_"), ONUs[[i]], gen_exp(lambda[i], burst)) # # env %>% # # OLT worker # add_generator("token_", OLT, at(RRH_on + Tg), mon=2) %>% # run(until=1e5/sum(lambda)) %>% # wrap() # } # # # grid of scenarios # cases <- data.frame(scenario = c(rep("A", 4), rep("B", 1)), # ONU_n = c(rep(31, 4), rep(7, 1)), # limit = c(Inf, 1500, 3000, 6000, Inf)) # # # parallel simulation # system.time({ # envs <- parallel::mclapply(split(cases, 1:nrow(cases)), simulate, # mc.cores=nrow(cases), mc.set.seed=FALSE) # }) ## ----2.analysis--------------------------------------------------------------- # library(tidyverse) # # bp.vals <- function(x, probs=c(0.05, 0.25, 0.5, 0.75, 0.95)) { # r <- quantile(x, probs=probs, na.rm=TRUE) # names(r) <- c("ymin", "lower", "middle", "upper", "ymax") # r # } # # envs %>% # get_mon_arrivals() %>% # left_join(rowid_to_column(cases, "replication")) %>% # mutate(scenario = forcats::fct_recode( # scenario, `SmallCell + 31 ONUs`="A", `RRH + 7 ONUs`="B")) %>% # separate(name, c("flow", "index", "n")) %>% # mutate(flow = if_else(index == ONU_n+1, "SmallCell", flow)) %>% # mutate(total_time = end_time - start_time, # waiting_time = total_time - activity_time) %>% # # plot # ggplot(aes(forcats::fct_rev(flow), waiting_time*1e6, color=factor(limit))) + theme_bw() + # facet_grid(~scenario, scales="free", space="free") + # stat_summary(fun.data=bp.vals, geom="boxplot", position="dodge") + # theme(legend.justification=c(0, 1), legend.position=c(.02, .98)) + # labs(y=expression(paste("Queueing Delay [", mu, "s]")), x=element_blank(), color="Limit [bytes]") ## ----3.configuration---------------------------------------------------------- # m <- 9 # number of RA retries # Ps <- 0.03 # sleep power consumption [mW] # Pi <- 10 # idle power consumption [mW] # Prx <- 100 # rx power consumption [mW] # Prbp <- 32.18 # tx power consumption per RB pair [mW] # Ppre <- 32.18 # preamble tx power consumption [mW] # # Tbpre <- 0.0025 # time before preamble transmission [s] # Tpre <- 0.0014 # time for preamble transmission [s] # Trarx <- 0.01 # rx time to perform RA [s] # Tcrcp <- 0.016 # sum of processing delays to establish a connection [s] # Twait <- 0.054 # S1 processing and transfer delay [s] # # Brbp <- 36 # bytes per RB pair (QPSK modulation) # Breq <- 7 # RRC request message size (bytes) # Bcompcp <- 129 # RRC setup complete + NAS control plane SR + data for CP (bytes) # # Tra <- Tbpre + Trarx + Tpre # Pra <- (Tbpre*Pi + Trarx*Prx + Tpre*Ppre) / Tra # rbs <- (ceiling(Breq/Brbp) + ceiling(Bcompcp/Brbp)) * 1e-3 # Ttx <- Tcrcp + rbs + Twait # Ptx <- (Tcrcp*Prx + rbs*Prbp + Twait*Prx) / Ttx # # tx_period <- 3600 # time between transmissions (seconds) # Tsim <- 24*3600 # simulation time (seconds) ## ----3.simulation------------------------------------------------------------- # library(simmer) # # set.seed(1234) # # # parametrized simulation function # # - param[["meter_n"]] is the number of IoT devices in the cell # # - param[["backoff"]] is the backoff time # simulate <- function(param) { # # # identifiers for RA preambles # preambles <- paste0("preamble_", 1:54) # # # IoT device logic # meter <- trajectory() %>% # trap("reading") %>% # # sleep # set_attribute("P", 0) %>% # wait() %>% # timeout(function() round(runif(1, 0, param[["backoff"]]), 3)) %>% # # ra start # simmer::select(preambles, policy="random") %>% # seize_selected( # continue=c(TRUE, TRUE), # # ra & tx # post.seize=trajectory() %>% # set_attribute("P", Pra) %>% # timeout(Tra) %>% # release_selected() %>% # set_attribute("P", Ptx) %>% # timeout(Ttx), # # ra & backoff & retry # reject=trajectory() %>% # set_attribute("P", Pra) %>% # timeout(Tra) %>% # set_attribute("P", Pi) %>% # timeout(function() sample(1:20, 1) * 1e-3) %>% # rollback(6, times=m) # ) %>% # rollback(5, times=Inf) # # # trigger a reading for all the meters every tx_period # trigger <- trajectory() %>% # timeout(tx_period) %>% # send("reading") %>% # rollback(2, times=Inf) # # # simulation environment # env <- simmer() %>% # # IoT device workers # add_generator("meter_", meter, at(rep(0, param[["meter_n"]])), mon=2) %>% # # trigger worker # add_generator("trigger_", trigger, at(0), mon=0) # # for (i in preambles) # # one resource per preamble # env %>% add_resource(i, 1, 0, mon=FALSE) # # env %>% # run(until=Tsim) %>% # wrap() # } # # # grid of scenarios # cases <- expand.grid(meter_n = c(5e3, 1e4, 3e4), backoff = c(5, 10, 30, 60)) # # # parallel simulation # system.time({ # envs <- parallel::mclapply(split(cases, 1:nrow(cases)), simulate, # mc.cores=nrow(cases), mc.set.seed=FALSE) # }) ## ----3.analysis--------------------------------------------------------------- # library(tidyverse) # # bp.vals <- function(x, probs=c(0.05, 0.25, 0.5, 0.75, 0.95)) { # r <- quantile(x, probs=probs, na.rm=TRUE) # names(r) <- c("ymin", "lower", "middle", "upper", "ymax") # r # } # # envs %>% # get_mon_attributes() %>% # group_by(replication, name) %>% # summarise(dE = sum(c(0, head(value, -1) * diff(time)))) %>% # left_join(rowid_to_column(cases, "replication")) %>% # # plot # ggplot(aes(factor(meter_n), dE*tx_period/Tsim, color=factor(backoff))) + # stat_summary(fun.data=bp.vals, geom="boxplot", position="dodge") + # theme_bw() + theme(legend.justification=c(0, 1), legend.position=c(.02, .98)) + # labs(y="Energy per Tx [mJ]", x="No. of devices", color="Backoff window [s]")