Skip to main content

des_sim/modeling/sampler/combinator/
jitter.rs

1//! The `jitter` module provides the `JitterSampler`, a combinator that adds
2//! a random or pseudo-random offset (jitter) to the output of a base
3//! `DurationSampler`.
4//!
5//! Unlike `DelaySampler`, `JitterSampler` allows for both positive and negative
6//! offsets, enabling the sampled duration to be either increased or decreased.
7
8use crate::modeling::sampler::{DurationSampler, PendingDuration};
9use crate::primitive::time::SimTime;
10use rand::Rng;
11
12/// A sampler that applies "jitter" (noise) to the output of a base sampler.
13///
14/// The jitter value is added directly to the base sampler's output. Negative jitter
15/// values will decrease the total duration (unlike [DelaySampler](crate::modeling::sampler::DelaySampler)).
16pub struct JitterSampler<S>
17where
18    S: DurationSampler,
19{
20    sampler: S,
21    jitter_sampler: Box<dyn DurationSampler>,
22}
23
24impl<S> DurationSampler for JitterSampler<S>
25where
26    S: DurationSampler,
27{
28    fn sample(&mut self, rng: &mut dyn Rng, current_tick: SimTime) -> PendingDuration {
29        let sampled = self.sampler.sample(rng, current_tick);
30        let jitter = self.jitter_sampler.sample(rng, current_tick);
31
32        PendingDuration::new(sampled.raw_value() + jitter.raw_value())
33    }
34}
35
36impl<S> JitterSampler<S>
37where
38    S: DurationSampler,
39{
40    /// Creates a new `JitterSampler`.
41    pub fn new(sampler: S, jitter_sampler: Box<dyn DurationSampler>) -> Self {
42        Self {
43            sampler,
44            jitter_sampler,
45        }
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52    use crate::modeling::sampler::instance::ConstantSampler;
53    use rand::SeedableRng;
54    use rand::rngs::SmallRng;
55
56    #[test]
57    fn test_jitter_sampler_positive_jitter() {
58        let mut rng = SmallRng::seed_from_u64(2);
59        let base_sampler = ConstantSampler::new(10.0);
60        let jitter_sampler = Box::new(ConstantSampler::new(5.0));
61        let mut sampler = JitterSampler::new(base_sampler, jitter_sampler);
62
63        let sample = sampler.sample(&mut rng, SimTime::from_ticks(0));
64        assert_eq!(sample.raw_value(), 15.0); // 10.0 + 5.0
65    }
66
67    #[test]
68    fn test_jitter_sampler_zero_jitter() {
69        let mut rng = SmallRng::seed_from_u64(2);
70        let base_sampler = ConstantSampler::new(10.0);
71        let jitter_sampler = Box::new(ConstantSampler::new(0.0));
72        let mut sampler = JitterSampler::new(base_sampler, jitter_sampler);
73
74        let sample = sampler.sample(&mut rng, SimTime::from_ticks(0));
75        assert_eq!(sample.raw_value(), 10.0); // 10.0 + 0.0
76    }
77
78    #[test]
79    fn test_jitter_sampler_negative_jitter() {
80        let mut rng = SmallRng::seed_from_u64(2);
81        let base_sampler = ConstantSampler::new(10.0);
82        let jitter_sampler = Box::new(ConstantSampler::new(-5.0));
83        let mut sampler = JitterSampler::new(base_sampler, jitter_sampler);
84
85        let sample = sampler.sample(&mut rng, SimTime::from_ticks(0));
86        assert_eq!(sample.raw_value(), 5.0); // 10.0 + (-5.0) = 5.0
87    }
88}