use pamoja_core::{Error, Result, Sensor};
#[derive(Clone, Copy, Debug)]
pub struct SimSensor {
value: f32,
drift: f32,
noise: f32,
rng: u32,
}
impl SimSensor {
pub fn new(baseline: f32) -> Self {
Self {
value: baseline,
drift: 0.0,
noise: 0.0,
rng: 0x9E37_79B9,
}
}
pub fn with_drift(mut self, per_read: f32) -> Self {
self.drift = per_read;
self
}
pub fn with_noise(mut self, amplitude: f32) -> Self {
self.noise = magnitude(amplitude);
self
}
pub fn with_seed(mut self, seed: u32) -> Self {
self.rng = if seed == 0 { 1 } else { seed };
self
}
fn next_noise(&mut self) -> f32 {
if self.noise == 0.0 {
return 0.0;
}
let mut x = self.rng;
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
self.rng = x;
let unit = (x as f32 / u32::MAX as f32) * 2.0 - 1.0; unit * self.noise
}
}
impl Sensor for SimSensor {
type Reading = f32;
async fn read(&mut self) -> Result<f32> {
let reading = self.value + self.next_noise();
self.value += self.drift;
Ok(reading)
}
}
#[derive(Clone, Debug)]
pub struct Replay {
readings: Vec<f32>,
index: usize,
repeat: bool,
}
impl Replay {
pub fn new(readings: Vec<f32>) -> Self {
Self {
readings,
index: 0,
repeat: false,
}
}
pub fn repeating(readings: Vec<f32>) -> Self {
Self {
readings,
index: 0,
repeat: true,
}
}
}
impl Sensor for Replay {
type Reading = f32;
async fn read(&mut self) -> Result<f32> {
if self.index >= self.readings.len() {
if self.repeat && !self.readings.is_empty() {
self.index = 0;
} else {
return Err(Error::Closed);
}
}
let reading = self.readings[self.index];
self.index += 1;
Ok(reading)
}
}
fn magnitude(value: f32) -> f32 {
if value < 0.0 {
-value
} else {
value
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn a_plain_sensor_returns_its_baseline() {
let mut sensor = SimSensor::new(3.5);
assert_eq!(sensor.read().await.unwrap(), 3.5);
assert_eq!(sensor.read().await.unwrap(), 3.5);
}
#[tokio::test]
async fn drift_accumulates_each_reading() {
let mut sensor = SimSensor::new(10.0).with_drift(2.0);
assert_eq!(sensor.read().await.unwrap(), 10.0);
assert_eq!(sensor.read().await.unwrap(), 12.0);
assert_eq!(sensor.read().await.unwrap(), 14.0);
}
#[tokio::test]
async fn the_same_seed_replays_the_same_noise() {
let mut a = SimSensor::new(20.0).with_noise(0.5).with_seed(7);
let mut b = SimSensor::new(20.0).with_noise(0.5).with_seed(7);
for _ in 0..16 {
assert_eq!(a.read().await.unwrap(), b.read().await.unwrap());
}
}
#[tokio::test]
async fn noise_stays_within_its_amplitude() {
let mut sensor = SimSensor::new(20.0).with_noise(0.5).with_seed(99);
for _ in 0..1000 {
let reading = sensor.read().await.unwrap();
assert!((reading - 20.0).abs() <= 0.5 + f32::EPSILON);
}
}
#[tokio::test]
async fn replay_yields_readings_in_order_then_closes() {
let mut sensor = Replay::new(vec![1.0, 2.0]);
assert_eq!(sensor.read().await.unwrap(), 1.0);
assert_eq!(sensor.read().await.unwrap(), 2.0);
assert!(matches!(sensor.read().await, Err(Error::Closed)));
}
#[tokio::test]
async fn a_repeating_replay_loops() {
let mut sensor = Replay::repeating(vec![1.0, 2.0]);
assert_eq!(sensor.read().await.unwrap(), 1.0);
assert_eq!(sensor.read().await.unwrap(), 2.0);
assert_eq!(sensor.read().await.unwrap(), 1.0);
}
#[tokio::test]
async fn an_empty_replay_is_closed() {
let mut sensor = Replay::repeating(vec![]);
assert!(matches!(sensor.read().await, Err(Error::Closed)));
}
}