Skip to content

Commit 00ea606

Browse files
committed
Adding log-normal per-packet delay
1 parent a14b1cb commit 00ea606

2 files changed

Lines changed: 192 additions & 5 deletions

File tree

src/model/delay_per_packet.rs

Lines changed: 188 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ pub trait DelayPerPacketTraceConfig: DynClone + Send {
7474
dyn_clone::clone_trait_object!(DelayPerPacketTraceConfig);
7575

7676
use rand::{rngs::StdRng, SeedableRng as _};
77-
use rand_distr::{Distribution, Normal};
77+
use rand_distr::{Distribution, LogNormal, Normal};
7878
#[cfg(feature = "serde")]
7979
use serde::{Deserialize, Serialize};
8080

@@ -211,7 +211,7 @@ pub struct RepeatedDelayPerPacketPatternConfig {
211211

212212
/// The model of a per-packet delay trace subjects to a normal distribution.
213213
///
214-
/// The delay will subject to N(mean, std_dev), but bounded within [lower_bound, upper_bound] (optional)
214+
/// The delay will subject to N(mean, std_dev²), but bounded within [lower_bound, upper_bound] (optional)
215215
///
216216
/// If the `count` is 0, the delay will be repeated forever, else it will be repeated `count` times.
217217
///
@@ -298,6 +298,97 @@ pub struct NormalizedDelayPerPacketConfig {
298298
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
299299
pub seed: Option<u64>,
300300
}
301+
/// The model of a per-packet delay trace subjects to a log-normal distribution.
302+
///
303+
/// The delay will subject to Lognormal(μ, σ²), but bounded within [lower_bound, upper_bound] (optional)
304+
/// The provided mean and std_dev are the one of the log-normal law and not the one of the underlying normal law.
305+
///
306+
/// If the `count` is 0, the delay will be repeated forever, else it will be repeated `count` times.
307+
///
308+
/// ## Examples
309+
///
310+
/// A simple example without any bound on delay:
311+
///
312+
/// ```
313+
/// # use netem_trace::model::LogNormalizedDelayPerPacketConfig;
314+
/// # use netem_trace::{Delay, DelayPerPacketTrace};
315+
/// let mut log_normal_delay = LogNormalizedDelayPerPacketConfig::new()
316+
/// .mean(Delay::from_millis(12))
317+
/// .std_dev(Delay::from_millis(1))
318+
/// .count(2)
319+
/// .seed(42)
320+
/// .build();
321+
/// assert_eq!(log_normal_delay.next_delay(), Some(Delay::from_nanos(12027817)));
322+
/// assert_eq!(log_normal_delay.next_delay(), Some(Delay::from_nanos(12091533)));
323+
/// assert_eq!(log_normal_delay.next_delay(), None);
324+
/// ```
325+
///
326+
/// A more complex example with bounds on delay:
327+
///
328+
/// ```
329+
/// # use netem_trace::model::LogNormalizedDelayPerPacketConfig;
330+
/// # use netem_trace::{Delay, DelayPerPacketTrace};
331+
/// let mut log_normal_delay = LogNormalizedDelayPerPacketConfig::new()
332+
/// .mean(Delay::from_millis(12))
333+
/// .std_dev(Delay::from_millis(1))
334+
/// .count(3)
335+
/// .seed(42)
336+
/// .upper_bound(Delay::from_micros(12100))
337+
/// .lower_bound(Delay::from_micros(11900))
338+
/// .build();
339+
/// assert_eq!(log_normal_delay.next_delay(), Some(Delay::from_nanos(12027817)));
340+
/// assert_eq!(log_normal_delay.next_delay(), Some(Delay::from_nanos(12091533)));
341+
/// assert_eq!(log_normal_delay.next_delay(), Some(Delay::from_nanos(12100000)));
342+
/// assert_eq!(log_normal_delay.next_delay(), None);
343+
/// ```
344+
#[derive(Debug, Clone)]
345+
pub struct LogNormalizedDelayPerPacket {
346+
pub mean: Delay,
347+
pub std_dev: Delay,
348+
pub upper_bound: Option<Delay>,
349+
pub lower_bound: Delay,
350+
pub seed: u64,
351+
pub count: usize,
352+
current_count: usize,
353+
rng: StdRng,
354+
log_normal: LogNormal<f64>,
355+
}
356+
357+
/// The configuration struct for [`LogNormalizedDelayPerPacket`].
358+
///
359+
/// See [`LogNormalizedDelayPerPacket`] for more details.
360+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(default))]
361+
#[derive(Debug, Clone, Default)]
362+
pub struct LogNormalizedDelayPerPacketConfig {
363+
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
364+
#[cfg_attr(
365+
all(feature = "serde", feature = "human"),
366+
serde(with = "humantime_serde")
367+
)]
368+
pub mean: Option<Delay>,
369+
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
370+
#[cfg_attr(
371+
all(feature = "serde", feature = "human"),
372+
serde(with = "humantime_serde")
373+
)]
374+
pub std_dev: Option<Delay>,
375+
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
376+
#[cfg_attr(
377+
all(feature = "serde", feature = "human"),
378+
serde(with = "humantime_serde")
379+
)]
380+
pub upper_bound: Option<Delay>,
381+
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
382+
#[cfg_attr(
383+
all(feature = "serde", feature = "human"),
384+
serde(with = "humantime_serde")
385+
)]
386+
pub lower_bound: Option<Delay>,
387+
#[cfg_attr(feature = "serde", serde(default))]
388+
pub count: usize,
389+
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
390+
pub seed: Option<u64>,
391+
}
301392

302393
impl DelayPerPacketTrace for StaticDelayPerPacket {
303394
fn next_delay(&mut self) -> Option<Delay> {
@@ -354,6 +445,23 @@ impl DelayPerPacketTrace for NormalizedDelayPerPacket {
354445
}
355446
}
356447

448+
impl DelayPerPacketTrace for LogNormalizedDelayPerPacket {
449+
fn next_delay(&mut self) -> Option<Delay> {
450+
if self.count != 0 && self.count == self.current_count {
451+
None
452+
} else {
453+
self.current_count += 1;
454+
let delay = self.log_normal.sample(&mut self.rng).max(0.0);
455+
let mut delay = Delay::from_secs_f64(delay);
456+
delay = delay.max(self.lower_bound);
457+
if let Some(upper_bound) = self.upper_bound {
458+
delay = delay.min(upper_bound);
459+
}
460+
Some(delay)
461+
}
462+
}
463+
}
464+
357465
impl StaticDelayPerPacketConfig {
358466
pub fn new() -> Self {
359467
Self {
@@ -546,6 +654,83 @@ impl NormalizedDelayPerPacketConfig {
546654
self.build()
547655
}
548656
}
657+
658+
impl LogNormalizedDelayPerPacketConfig {
659+
pub fn new() -> Self {
660+
Self {
661+
mean: None,
662+
std_dev: None,
663+
upper_bound: None,
664+
lower_bound: None,
665+
count: 0,
666+
seed: None,
667+
}
668+
}
669+
670+
pub fn mean(mut self, mean: Delay) -> Self {
671+
self.mean = Some(mean);
672+
self
673+
}
674+
675+
pub fn std_dev(mut self, std_dev: Delay) -> Self {
676+
self.std_dev = Some(std_dev);
677+
self
678+
}
679+
680+
pub fn upper_bound(mut self, upper_bound: Delay) -> Self {
681+
self.upper_bound = Some(upper_bound);
682+
self
683+
}
684+
685+
pub fn lower_bound(mut self, lower_bound: Delay) -> Self {
686+
self.lower_bound = Some(lower_bound);
687+
self
688+
}
689+
690+
pub fn count(mut self, count: usize) -> Self {
691+
self.count = count;
692+
self
693+
}
694+
695+
pub fn seed(mut self, seed: u64) -> Self {
696+
self.seed = Some(seed);
697+
self
698+
}
699+
700+
pub fn random_seed(mut self) -> Self {
701+
self.seed = Some(rand::random());
702+
self
703+
}
704+
705+
pub fn build(&self) -> LogNormalizedDelayPerPacket {
706+
let mean = self.mean.unwrap_or_else(|| Delay::from_millis(10));
707+
let std_dev = self.std_dev.unwrap_or(Delay::ZERO);
708+
let upper_bound = self.upper_bound;
709+
let lower_bound = self.lower_bound.unwrap_or(Delay::ZERO);
710+
let count = self.count;
711+
let seed = self.seed.unwrap_or(DEFAULT_RNG_SEED);
712+
let rng = StdRng::seed_from_u64(seed);
713+
let delay_mean = mean.as_secs_f64();
714+
let delay_std_dev = std_dev.as_secs_f64();
715+
let normal_std_dev = f64::sqrt(f64::ln(
716+
1.0 + (delay_std_dev.powi(2)) / (delay_mean.powi(2)),
717+
));
718+
let normal_mean = f64::ln(delay_mean) - normal_std_dev.powi(2) / 2.;
719+
let log_normal: LogNormal<f64> = LogNormal::new(normal_mean, normal_std_dev).unwrap();
720+
LogNormalizedDelayPerPacket {
721+
mean,
722+
std_dev,
723+
upper_bound,
724+
lower_bound,
725+
count,
726+
current_count: 0,
727+
seed,
728+
rng,
729+
log_normal,
730+
}
731+
}
732+
}
733+
549734
macro_rules! impl_delay_per_packet_trace_config {
550735
($name:ident) => {
551736
#[cfg_attr(feature = "serde", typetag::serde)]
@@ -559,6 +744,7 @@ macro_rules! impl_delay_per_packet_trace_config {
559744

560745
impl_delay_per_packet_trace_config!(StaticDelayPerPacketConfig);
561746
impl_delay_per_packet_trace_config!(NormalizedDelayPerPacketConfig);
747+
impl_delay_per_packet_trace_config!(LogNormalizedDelayPerPacketConfig);
562748
impl_delay_per_packet_trace_config!(RepeatedDelayPerPacketPatternConfig);
563749

564750
/// Turn a [`DelayPerPacketTraceConfig`] into a forever repeated [`RepeatedDelayPerPacketPatternConfig`].

src/model/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,13 @@ pub mod delay_per_packet;
3535

3636
#[cfg(feature = "delay-per-packet-model")]
3737
pub use delay_per_packet::{
38-
DelayPerPacketTraceConfig, NormalizedDelayPerPacketConfig, RepeatedDelayPerPacketPatternConfig,
39-
StaticDelayPerPacketConfig,
38+
DelayPerPacketTraceConfig, LogNormalizedDelayPerPacketConfig, NormalizedDelayPerPacketConfig,
39+
RepeatedDelayPerPacketPatternConfig, StaticDelayPerPacketConfig,
4040
};
4141
#[cfg(feature = "delay-per-packet-model")]
4242
pub use delay_per_packet::{
43-
NormalizedDelayPerPacket, RepeatedDelayPerPacketPattern, StaticDelayPerPacket,
43+
LogNormalizedDelayPerPacket, NormalizedDelayPerPacket, RepeatedDelayPerPacketPattern,
44+
StaticDelayPerPacket,
4445
};
4546

4647
#[cfg(feature = "loss-model")]

0 commit comments

Comments
 (0)