use std::cell::RefCell;
use crate::devices::Device;
use crate::error::Error;
use crate::matrix::{FlickerGen, NoiseCtx, NoiseGen, Var};
use crate::state::States;
use crate::trnoise::{GRID_TOL, MAX_ONEF_SAMPLES, Stream, kasdin, stream_key};
const SUB_DEV_WHITE: u64 = 0x100;
const SUB_DEV_FLICKER: u64 = 0x1_0000;
enum Kind {
White,
Flicker { ef: f64, y: Vec<f64> },
}
struct Gen {
n1: Var,
n2: Var,
kind: Kind,
key: u64,
amp_prev: f64,
amp_cur: f64,
}
impl Gen {
fn unit(&self, k: u64) -> f64 {
if k == 0 {
return 0.0;
}
match &self.kind {
Kind::White => Stream::at(self.key, k).gauss(),
Kind::Flicker { y, .. } => y[(k as usize).min(y.len() - 1)],
}
}
fn amp(&self, d: f64, nt: f64) -> f64 {
let d = d.abs();
if !d.is_finite() {
return 0.0;
}
match &self.kind {
Kind::White => (d / (2.0 * nt)).sqrt(),
Kind::Flicker { ef, .. } => {
(d * (2.0 * std::f64::consts::PI * nt).powf(*ef) / (2.0 * nt)).sqrt()
}
}
}
}
pub(crate) struct DevNoise {
nt: f64,
seed: u64,
temp: f64,
final_time: f64,
devs: Vec<(usize, usize, usize)>,
gens: Vec<Gen>,
k: Option<u64>,
zeros: Vec<f64>,
notes: Vec<String>,
}
fn record(
d: &dyn Device,
zeros: &[f64],
temp: f64,
states: &States,
) -> (Vec<NoiseGen>, Vec<FlickerGen>) {
let white = RefCell::new(Vec::new());
let flicker = RefCell::new(Vec::new());
let ctx = NoiseCtx {
re: zeros,
im: zeros,
freq: 1.0,
temp,
states,
sp_sink: Some(&white),
flicker_sink: Some(&flicker),
};
d.noise_dens(&ctx);
(white.into_inner(), flicker.into_inner())
}
impl DevNoise {
pub(crate) fn new(nt: f64, seed: u64, temp: f64, final_time: f64, nvars: usize) -> DevNoise {
DevNoise {
nt,
seed,
temp,
final_time,
devs: Vec::new(),
gens: Vec::new(),
k: None,
zeros: vec![0.0; nvars],
notes: Vec::new(),
}
}
fn collect(
&mut self,
devices: &[Box<dyn Device>],
names: &[String],
states: &States,
) -> Result<(), Error> {
let n_onef = (self.final_time / self.nt).floor() + 10.0;
let mut unsupported: Vec<String> = Vec::new();
for (i, d) in devices.iter().enumerate() {
if d.noise_suffixes().is_none() {
continue;
}
let (white, flicker) = record(d.as_ref(), &self.zeros, self.temp, states);
let first = self.gens.len();
let name = &names[i];
for (j, &(n1, n2, _)) in white.iter().enumerate() {
self.gens.push(Gen {
n1,
n2,
kind: Kind::White,
key: stream_key(self.seed, name, SUB_DEV_WHITE + j as u64),
amp_prev: 0.0,
amp_cur: 0.0,
});
}
for (j, f) in flicker.iter().enumerate() {
match *f {
FlickerGen::PowerLaw { n1, n2, ef, .. } => {
if n_onef > MAX_ONEF_SAMPLES {
return Err(Error::Build(format!(
"device `{name}`: transient flicker noise needs more than \
{MAX_ONEF_SAMPLES} samples (TSTOP/DEVNT); refusing"
)));
}
let key = stream_key(self.seed, name, SUB_DEV_FLICKER + j as u64);
let y = kasdin(n_onef as usize, 1.0, ef, key);
let y0 = y[0];
self.gens.push(Gen {
n1,
n2,
kind: Kind::Flicker {
ef,
y: y.iter().map(|v| v - y0).collect(),
},
key,
amp_prev: 0.0,
amp_cur: 0.0,
});
}
FlickerGen::Unsupported(what) => unsupported.push(format!("`{name}` ({what})")),
}
}
self.devs.push((i, first, self.gens.len() - first));
}
let live = self.gens.iter().filter(|g| g.n1 != g.n2).count();
self.notes.push(format!(
"device noise: {live} generator(s) on {} device(s), devnt {:e} s, seed {} \
(stream version {})",
self.devs.len(),
self.nt,
self.seed,
crate::trnoise::STREAM_VERSION
));
if !unsupported.is_empty() {
self.notes.push(format!(
"device noise: no transient form for {}; left out",
unsupported.join(", ")
));
}
Ok(())
}
fn refresh(&mut self, devices: &[Box<dyn Device>], states: &States) {
for &(i, first, count) in &self.devs {
let (white, flicker) = record(devices[i].as_ref(), &self.zeros, self.temp, states);
let dens = white
.iter()
.map(|&(_, _, d)| d)
.chain(flicker.iter().filter_map(|f| match *f {
FlickerGen::PowerLaw { coef, .. } => Some(coef),
FlickerGen::Unsupported(_) => None,
}));
let mut n = 0;
for (g, d) in self.gens[first..first + count].iter_mut().zip(dens) {
g.amp_cur = g.amp(d, self.nt);
n += 1;
}
debug_assert_eq!(n, count, "device {i} changed its generator count");
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn accept(
&mut self,
time: f64,
min_break: f64,
devices: &[Box<dyn Device>],
names: &[String],
states: &States,
requests: &mut Vec<(f64, bool)>,
) -> Result<(), Error> {
let x = time / self.nt;
let kk = (x + GRID_TOL).floor().max(0.0) as u64;
match self.k {
None => {
self.collect(devices, names, states)?;
self.refresh(devices, states);
for g in &mut self.gens {
g.amp_prev = g.amp_cur;
}
self.k = Some(kk);
}
Some(k) if kk > k => {
for g in &mut self.gens {
g.amp_prev = g.amp_cur;
}
self.refresh(devices, states);
self.k = Some(kk);
}
Some(_) => {}
}
let kb = ((time + min_break) / self.nt + GRID_TOL).floor();
let next = (kb + 1.0) * self.nt;
if next < self.final_time {
requests.push((next, false));
}
Ok(())
}
pub(crate) fn stamp(&self, time: f64, rhs: &mut [f64]) {
let Some(k) = self.k else {
return;
};
let frac = (time / self.nt - k as f64).clamp(0.0, 1.0);
for g in &self.gens {
if g.n1 == g.n2 || (g.amp_prev == 0.0 && g.amp_cur == 0.0) {
continue;
}
let x0 = g.amp_prev * g.unit(k);
let x1 = g.amp_cur * g.unit(k + 1);
let i = x0 + (x1 - x0) * frac;
rhs[g.n1] -= i;
rhs[g.n2] += i;
}
}
pub(crate) fn notes(&self) -> &[String] {
&self.notes
}
}