feat(autorouter/multilayer_reconfigurator): Add tracking of SMA of rate

SMA = Simple Moving Average
This commit is contained in:
Mikolaj Wielgus 2025-11-03 01:26:49 +01:00
parent 0f3f96d4af
commit eff58d99e3
12 changed files with 218 additions and 76 deletions

View File

@ -4,7 +4,10 @@
use std::ops::ControlFlow; use std::ops::ControlFlow;
use topola::{interactor::activity::ActivityStepperWithStatus, stepper::EstimateLinearProgress}; use topola::{
interactor::activity::ActivityStepperWithStatus,
stepper::{EstimateProgress, GetMaybeReconfigurationTriggerProgress},
};
use crate::{translator::Translator, viewport::Viewport}; use crate::{translator::Translator, viewport::Viewport};
@ -40,21 +43,32 @@ impl StatusBar {
)); ));
if let Some(activity) = maybe_activity { if let Some(activity) = maybe_activity {
let linear_progress = activity.estimate_linear_progress(); let progress = activity.estimate_progress();
let value = linear_progress.value(); let value = progress.value();
let maximum = linear_progress.maximum(); let maximum = progress.reference();
let ratio = *value as f32 / *maximum as f32; let ratio = *value as f32 / *maximum as f32;
ui.add(egui::ProgressBar::new(ratio).text(format!( if let Some(trigger_progress) = activity.reconfiguration_trigger_progress() {
"{:.1}% ({:.1}/{:.1})", ui.add(egui::ProgressBar::new(ratio).text(format!(
ratio * 100.0, "{:.1}% ({:.1}/{:.1}) (sma: {:.1}, min: {:.1}))",
value, ratio * 100.0,
maximum value,
))); maximum,
trigger_progress.value(),
trigger_progress.reference(),
)));
} else {
ui.add(egui::ProgressBar::new(ratio).text(format!(
"{:.1}% ({:.1}/{:.1})",
ratio * 100.0,
value,
maximum
)));
}
let linear_subprogress = linear_progress.subscale(); let linear_subprogress = progress.subscale();
let value = linear_subprogress.value(); let value = linear_subprogress.value();
let maximum = linear_subprogress.maximum(); let maximum = linear_subprogress.reference();
let ratio = *value as f32 / *maximum as f32; let ratio = *value as f32 / *maximum as f32;
ui.add(egui::ProgressBar::new(ratio).text(format!( ui.add(egui::ProgressBar::new(ratio).text(format!(

View File

@ -39,7 +39,7 @@ pub struct Workspace {
Receiver<std::io::Result<Result<History, serde_json::Error>>>, Receiver<std::io::Result<Result<History, serde_json::Error>>>,
), ),
update_counter: f32, dt_accumulator: f32,
} }
impl Workspace { impl Workspace {
@ -65,7 +65,7 @@ impl Workspace {
) )
})?, })?,
history_channel: channel(), history_channel: channel(),
update_counter: 0.0, dt_accumulator: 0.0,
}) })
} }
@ -82,12 +82,12 @@ impl Workspace {
let instant = Instant::now(); let instant = Instant::now();
if maybe_step_rate.is_some() { if maybe_step_rate.is_some() {
self.update_counter += interactive_input.dt; self.dt_accumulator += interactive_input.dt;
} }
while maybe_step_rate.is_none_or(|step_rate| self.update_counter >= 1.0 / step_rate) { while maybe_step_rate.is_none_or(|step_rate| self.dt_accumulator >= 1.0 / step_rate) {
if let Some(step_rate) = maybe_step_rate { if let Some(step_rate) = maybe_step_rate {
self.update_counter -= 1.0 / step_rate; self.dt_accumulator -= 1.0 / step_rate;
} }
if let ControlFlow::Break(()) = self.update_state(tr, error_dialog, interactive_input) { if let ControlFlow::Break(()) = self.update_state(tr, error_dialog, interactive_input) {

View File

@ -16,7 +16,7 @@ use crate::{
board::{edit::BoardEdit, AccessMesadata}, board::{edit::BoardEdit, AccessMesadata},
layout::via::ViaWeight, layout::via::ViaWeight,
router::ng, router::ng,
stepper::{Abort, EstimateLinearProgress, LinearScale, Step}, stepper::{Abort, EstimateProgress, GetMaybeReconfigurationTriggerProgress, LinearScale, Step},
}; };
use super::{ use super::{
@ -173,16 +173,14 @@ impl<M: AccessMesadata + Clone> Abort<Invoker<M>> for ExecutionStepper<M> {
// Since enum_dispatch does not really support generics, we implement this the // Since enum_dispatch does not really support generics, we implement this the
// long way by using `match`. // long way by using `match`.
impl<M> EstimateLinearProgress for ExecutionStepper<M> { impl<M> EstimateProgress for ExecutionStepper<M> {
type Value = usize; type Value = usize;
type Subscale = LinearScale<f64>; type Subscale = LinearScale<f64>;
fn estimate_linear_progress(&self) -> LinearScale<usize, LinearScale<f64>> { fn estimate_progress(&self) -> LinearScale<usize, LinearScale<f64>> {
match self { match self {
ExecutionStepper::MultilayerAutoroute(autoroute) => { ExecutionStepper::MultilayerAutoroute(autoroute) => autoroute.estimate_progress(),
autoroute.estimate_linear_progress() ExecutionStepper::PlanarAutoroute(autoroute) => autoroute.estimate_progress(),
}
ExecutionStepper::PlanarAutoroute(autoroute) => autoroute.estimate_linear_progress(),
ExecutionStepper::TopoAutoroute(..) => { ExecutionStepper::TopoAutoroute(..) => {
LinearScale::new(0, 0, LinearScale::new(0.0, 0.0, ())) LinearScale::new(0, 0, LinearScale::new(0.0, 0.0, ()))
} }
@ -198,3 +196,22 @@ impl<M> EstimateLinearProgress for ExecutionStepper<M> {
} }
} }
} }
// Since enum_dispatch does not really support generics, we implement this the
// long way by using `match`.
impl<M> GetMaybeReconfigurationTriggerProgress for ExecutionStepper<M> {
type Subscale = ();
fn reconfiguration_trigger_progress(&self) -> Option<LinearScale<f64>> {
match self {
ExecutionStepper::MultilayerAutoroute(autoroute) => {
autoroute.reconfiguration_trigger_progress()
}
ExecutionStepper::PlanarAutoroute(autoroute) => None,
ExecutionStepper::TopoAutoroute(..) => None,
ExecutionStepper::PlaceVia(..) => None,
ExecutionStepper::RemoveBands(..) => None,
ExecutionStepper::MeasureLength(..) => None,
}
}
}

View File

@ -21,9 +21,7 @@ use crate::{
drawing::graph::PrimitiveIndex, drawing::graph::PrimitiveIndex,
geometry::{edit::Edit, primitive::PrimitiveShape}, geometry::{edit::Edit, primitive::PrimitiveShape},
router::{navcord::Navcord, navmesh::Navmesh, thetastar::ThetastarStepper}, router::{navcord::Navcord, navmesh::Navmesh, thetastar::ThetastarStepper},
stepper::{ stepper::{Abort, EstimateProgress, LinearScale, ReconfiguratorStatus, Reconfigure, Step},
Abort, EstimateLinearProgress, LinearScale, ReconfiguratorStatus, Reconfigure, Step,
},
}; };
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -119,12 +117,12 @@ impl<M: AccessMesadata> Reconfigure<Autorouter<M>> for MultilayerAutorouteExecut
} }
} }
impl EstimateLinearProgress for MultilayerAutorouteExecutionStepper { impl EstimateProgress for MultilayerAutorouteExecutionStepper {
type Value = usize; type Value = usize;
type Subscale = LinearScale<f64>; type Subscale = LinearScale<f64>;
fn estimate_linear_progress(&self) -> LinearScale<usize, LinearScale<f64>> { fn estimate_progress(&self) -> LinearScale<usize, LinearScale<f64>> {
self.planar.estimate_linear_progress() self.planar.estimate_progress()
} }
} }

View File

@ -30,7 +30,8 @@ use crate::{
geometry::primitive::PrimitiveShape, geometry::primitive::PrimitiveShape,
router::{navcord::Navcord, navmesh::Navmesh, thetastar::ThetastarStepper}, router::{navcord::Navcord, navmesh::Navmesh, thetastar::ThetastarStepper},
stepper::{ stepper::{
Abort, EstimateLinearProgress, LinearScale, ReconfiguratorStatus, Reconfigure, Step, Abort, EstimateProgress, GetMaybeReconfigurationTriggerProgress, LinearScale,
ReconfiguratorStatus, Reconfigure, SmaRateReconfigurationTrigger, Step,
}, },
}; };
@ -39,6 +40,7 @@ pub type MultilayerReconfiguratorStatus =
pub struct MultilayerAutorouteReconfigurator { pub struct MultilayerAutorouteReconfigurator {
stepper: MultilayerAutorouteExecutionStepper, stepper: MultilayerAutorouteExecutionStepper,
reconfiguration_trigger: SmaRateReconfigurationTrigger,
reconfigurer: MultilayerAutorouteReconfigurer, reconfigurer: MultilayerAutorouteReconfigurer,
options: MultilayerAutorouteOptions, options: MultilayerAutorouteOptions,
} }
@ -72,6 +74,7 @@ impl MultilayerAutorouteReconfigurator {
preconfiguration, preconfiguration,
options, options,
)?, )?,
reconfiguration_trigger: SmaRateReconfigurationTrigger::new(10, 0.5, 0.5),
reconfigurer, reconfigurer,
options, options,
}) })
@ -83,6 +86,9 @@ impl MultilayerAutorouteReconfigurator {
planar_result: Result<PlanarAutorouteConfigurationStatus, AutorouterError>, planar_result: Result<PlanarAutorouteConfigurationStatus, AutorouterError>,
) -> Result<ControlFlow<Option<BoardEdit>, MultilayerReconfiguratorStatus>, AutorouterError> ) -> Result<ControlFlow<Option<BoardEdit>, MultilayerReconfiguratorStatus>, AutorouterError>
{ {
// Reset the reconfiguration trigger.
self.reconfiguration_trigger = SmaRateReconfigurationTrigger::new(4, 0.5, 0.5);
loop { loop {
let configuration = match self let configuration = match self
.reconfigurer .reconfigurer
@ -120,6 +126,9 @@ impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, MultilayerReconfi
autorouter: &mut Autorouter<M>, autorouter: &mut Autorouter<M>,
) -> Result<ControlFlow<Option<BoardEdit>, MultilayerReconfiguratorStatus>, AutorouterError> ) -> Result<ControlFlow<Option<BoardEdit>, MultilayerReconfiguratorStatus>, AutorouterError>
{ {
self.reconfiguration_trigger
.update(*self.estimate_progress().value() as f64);
match self.stepper.step(autorouter) { match self.stepper.step(autorouter) {
Ok(ControlFlow::Break(maybe_edit)) => Ok(ControlFlow::Break(maybe_edit)), Ok(ControlFlow::Break(maybe_edit)) => Ok(ControlFlow::Break(maybe_edit)),
Ok(ControlFlow::Continue(ReconfiguratorStatus::Running(status))) => { Ok(ControlFlow::Continue(ReconfiguratorStatus::Running(status))) => {
@ -141,12 +150,24 @@ impl<M: AccessMesadata> Abort<Autorouter<M>> for MultilayerAutorouteReconfigurat
} }
} }
impl EstimateLinearProgress for MultilayerAutorouteReconfigurator { impl EstimateProgress for MultilayerAutorouteReconfigurator {
type Value = usize; type Value = usize;
type Subscale = LinearScale<f64>; type Subscale = LinearScale<f64>;
fn estimate_linear_progress(&self) -> LinearScale<usize, LinearScale<f64>> { fn estimate_progress(&self) -> LinearScale<usize, LinearScale<f64>> {
self.stepper.estimate_linear_progress() self.stepper.estimate_progress()
}
}
impl GetMaybeReconfigurationTriggerProgress for MultilayerAutorouteReconfigurator {
type Subscale = ();
fn reconfiguration_trigger_progress(&self) -> Option<LinearScale<f64>> {
Some(LinearScale::new(
(*self.reconfiguration_trigger.maybe_sma_rate_per_sec())?,
*self.reconfiguration_trigger.min_sma_rate_per_sec(),
(),
))
} }
} }

View File

@ -21,7 +21,7 @@ use crate::{
router::{ router::{
navcord::Navcord, navmesh::Navmesh, thetastar::ThetastarStepper, RouteStepper, Router, navcord::Navcord, navmesh::Navmesh, thetastar::ThetastarStepper, RouteStepper, Router,
}, },
stepper::{Abort, EstimateLinearProgress, LinearScale, Reconfigure, Step}, stepper::{Abort, EstimateProgress, LinearScale, Reconfigure, Step},
}; };
use super::{ use super::{
@ -303,21 +303,21 @@ impl<M: AccessMesadata> Reconfigure<Autorouter<M>> for PlanarAutorouteExecutionS
} }
} }
impl EstimateLinearProgress for PlanarAutorouteExecutionStepper { impl EstimateProgress for PlanarAutorouteExecutionStepper {
type Value = usize; type Value = usize;
type Subscale = LinearScale<f64>; type Subscale = LinearScale<f64>;
fn estimate_linear_progress(&self) -> LinearScale<usize, LinearScale<f64>> { fn estimate_progress(&self) -> LinearScale<usize, LinearScale<f64>> {
LinearScale::new( LinearScale::new(
self.curr_ratline_index, self.curr_ratline_index,
self.configuration().ratlines.len(), self.configuration().ratlines.len(),
LinearScale::new( LinearScale::new(
self.route self.route
.as_ref() .as_ref()
.map_or(0.0, |route| *route.estimate_linear_progress().value()), .map_or(0.0, |route| *route.estimate_progress().value()),
self.route self.route
.as_ref() .as_ref()
.map_or(0.0, |route| *route.estimate_linear_progress().maximum()), .map_or(0.0, |route| *route.estimate_progress().reference()),
(), (),
), ),
) )

View File

@ -24,9 +24,7 @@ use crate::{
drawing::graph::PrimitiveIndex, drawing::graph::PrimitiveIndex,
geometry::primitive::PrimitiveShape, geometry::primitive::PrimitiveShape,
router::{navcord::Navcord, navmesh::Navmesh, thetastar::ThetastarStepper}, router::{navcord::Navcord, navmesh::Navmesh, thetastar::ThetastarStepper},
stepper::{ stepper::{Abort, EstimateProgress, LinearScale, ReconfiguratorStatus, Reconfigure, Step},
Abort, EstimateLinearProgress, LinearScale, ReconfiguratorStatus, Reconfigure, Step,
},
}; };
pub type PlanarAutorouteReconfiguratorStatus = pub type PlanarAutorouteReconfiguratorStatus =
@ -120,12 +118,12 @@ impl<M: AccessMesadata> Abort<Autorouter<M>> for PlanarAutorouteReconfigurator {
} }
} }
impl EstimateLinearProgress for PlanarAutorouteReconfigurator { impl EstimateProgress for PlanarAutorouteReconfigurator {
type Value = usize; type Value = usize;
type Subscale = LinearScale<f64>; type Subscale = LinearScale<f64>;
fn estimate_linear_progress(&self) -> LinearScale<usize, LinearScale<f64>> { fn estimate_progress(&self) -> LinearScale<usize, LinearScale<f64>> {
self.stepper.estimate_linear_progress() self.stepper.estimate_progress()
} }
} }

View File

@ -25,7 +25,9 @@ use crate::{
ng, ng,
thetastar::ThetastarStepper, thetastar::ThetastarStepper,
}, },
stepper::{Abort, EstimateLinearProgress, LinearScale, OnEvent, Step}, stepper::{
Abort, EstimateProgress, GetMaybeReconfigurationTriggerProgress, LinearScale, OnEvent, Step,
},
}; };
/// Stores the interactive input data from the user. /// Stores the interactive input data from the user.
@ -99,17 +101,30 @@ impl<M: AccessMesadata + Clone> Abort<Invoker<M>> for ActivityStepper<M> {
} }
// Since enum_dispatch does not really support generics, we implement this the // Since enum_dispatch does not really support generics, we implement this the
// long way. // long way by using `match`.
impl<M> EstimateLinearProgress for ActivityStepper<M> { impl<M> EstimateProgress for ActivityStepper<M> {
type Value = usize; type Value = usize;
type Subscale = LinearScale<f64>; type Subscale = LinearScale<f64>;
fn estimate_linear_progress(&self) -> LinearScale<usize, LinearScale<f64>> { fn estimate_progress(&self) -> LinearScale<usize, LinearScale<f64>> {
match self { match self {
ActivityStepper::Interaction(..) => { ActivityStepper::Interaction(..) => {
LinearScale::new(0, 0, LinearScale::new(0.0, 0.0, ())) LinearScale::new(0, 0, LinearScale::new(0.0, 0.0, ()))
} }
ActivityStepper::Execution(execution) => execution.estimate_linear_progress(), ActivityStepper::Execution(execution) => execution.estimate_progress(),
}
}
}
// Since enum_dispatch does not really support generics, we implement this the
// long way by using `match`.
impl<M> GetMaybeReconfigurationTriggerProgress for ActivityStepper<M> {
type Subscale = ();
fn reconfiguration_trigger_progress(&self) -> Option<LinearScale<f64>> {
match self {
ActivityStepper::Interaction(..) => None,
ActivityStepper::Execution(execution) => execution.reconfiguration_trigger_progress(),
} }
} }
} }
@ -195,12 +210,20 @@ impl<M: AccessMesadata + Clone> OnEvent<ActivityContext<'_, M>, InteractiveEvent
} }
} }
impl<M> EstimateLinearProgress for ActivityStepperWithStatus<M> { impl<M> EstimateProgress for ActivityStepperWithStatus<M> {
type Value = usize; type Value = usize;
type Subscale = LinearScale<f64>; type Subscale = LinearScale<f64>;
fn estimate_linear_progress(&self) -> LinearScale<usize, LinearScale<f64>> { fn estimate_progress(&self) -> LinearScale<usize, LinearScale<f64>> {
self.activity.estimate_linear_progress() self.activity.estimate_progress()
}
}
impl<M> GetMaybeReconfigurationTriggerProgress for ActivityStepperWithStatus<M> {
type Subscale = ();
fn reconfiguration_trigger_progress(&self) -> Option<LinearScale<f64>> {
self.activity.reconfiguration_trigger_progress()
} }
} }

View File

@ -16,7 +16,7 @@ use crate::{
geometry::primitive::PrimitiveShape, geometry::primitive::PrimitiveShape,
graph::GenericIndex, graph::GenericIndex,
layout::{poly::PolyWeight, Layout}, layout::{poly::PolyWeight, Layout},
stepper::{Abort, EstimateLinearProgress}, stepper::{Abort, EstimateProgress},
}; };
use super::{ use super::{

View File

@ -19,7 +19,7 @@ use crate::{
thetastar::{ThetastarError, ThetastarStepper}, thetastar::{ThetastarError, ThetastarStepper},
Router, RouterThetastarStrategy, Router, RouterThetastarStrategy,
}, },
stepper::{EstimateLinearProgress, LinearScale, Step}, stepper::{EstimateProgress, LinearScale, Step},
}; };
#[derive(Getters, Dissolve)] #[derive(Getters, Dissolve)]
@ -99,11 +99,11 @@ impl<R: AccessRules> Step<Router<'_, R>, BandTermsegIndex> for RouteStepper {
} }
} }
impl EstimateLinearProgress for RouteStepper { impl EstimateProgress for RouteStepper {
type Value = f64; type Value = f64;
type Subscale = (); type Subscale = ();
fn estimate_linear_progress(&self) -> LinearScale<f64> { fn estimate_progress(&self) -> LinearScale<f64> {
self.thetastar.estimate_linear_progress() self.thetastar.estimate_progress()
} }
} }

View File

@ -20,7 +20,7 @@ use thiserror::Error;
use std::cmp::Ordering; use std::cmp::Ordering;
use crate::stepper::{EstimateLinearProgress, LinearScale, Step}; use crate::stepper::{EstimateProgress, LinearScale, Step};
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct MinScored<K, T>(pub K, pub T); pub struct MinScored<K, T>(pub K, pub T);
@ -443,7 +443,7 @@ where
} }
} }
impl<G, K> EstimateLinearProgress for ThetastarStepper<G, K> impl<G, K> EstimateProgress for ThetastarStepper<G, K>
where where
G: GraphBase, G: GraphBase,
G::NodeId: Eq + Ord, G::NodeId: Eq + Ord,
@ -453,7 +453,7 @@ where
type Value = K; type Value = K;
type Subscale = (); type Subscale = ();
fn estimate_linear_progress(&self) -> LinearScale<Self::Value> { fn estimate_progress(&self) -> LinearScale<Self::Value> {
LinearScale::new( LinearScale::new(
self.progress_estimate_value, self.progress_estimate_value,
self.progress_estimate_maximum, self.progress_estimate_maximum,

View File

@ -3,6 +3,7 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
use core::ops::ControlFlow; use core::ops::ControlFlow;
use std::{collections::VecDeque, time::Instant};
use derive_getters::Getters; use derive_getters::Getters;
@ -86,34 +87,104 @@ pub trait OnEvent<Ctx, Event> {
#[derive(Clone, Copy, Debug, Getters)] #[derive(Clone, Copy, Debug, Getters)]
pub struct LinearScale<V, S = ()> { pub struct LinearScale<V, S = ()> {
value: V, value: V,
maximum: V, reference: V,
subscale: S, subscale: S,
} }
impl<V, S> LinearScale<V, S> { impl<V, S> LinearScale<V, S> {
pub fn new(value: V, maximum: V, subscale: S) -> Self { pub fn new(value: V, reference: V, subscale: S) -> Self {
Self { Self {
value, value,
maximum, reference,
subscale, subscale,
} }
} }
} }
impl<V: Default> Default for LinearScale<V> {
fn default() -> Self {
Self {
value: V::default(),
maximum: V::default(),
subscale: (),
}
}
}
/// Some steppers report estimates of how far they are from completion. /// Some steppers report estimates of how far they are from completion.
pub trait EstimateLinearProgress { pub trait EstimateProgress {
type Value; type Value;
type Subscale; type Subscale;
fn estimate_linear_progress(&self) -> LinearScale<Self::Value, Self::Subscale>; fn estimate_progress(&self) -> LinearScale<Self::Value, Self::Subscale>;
}
pub trait GetMaybeReconfigurationTriggerProgress {
type Subscale;
fn reconfiguration_trigger_progress(&self) -> Option<LinearScale<f64, Self::Subscale>>;
}
#[derive(Clone, Debug, Getters)]
pub struct SmaRateReconfigurationTrigger {
#[getter(skip)]
sample_buffer: VecDeque<f64>,
#[getter(skip)]
last_instant: Instant,
#[getter(skip)]
last_value: f64,
maybe_sma_rate_per_sec: Option<f64>,
#[getter(skip)]
sample_buffer_size: usize,
#[getter(skip)]
sampling_interval_secs: f64,
min_sma_rate_per_sec: f64,
}
impl SmaRateReconfigurationTrigger {
pub fn new(
sample_buffer_size: usize,
sampling_interval_secs: f64,
min_sma_rate_per_sec: f64,
) -> Self {
Self {
sample_buffer: VecDeque::new(),
last_instant: Instant::now(),
last_value: 0.0,
maybe_sma_rate_per_sec: None,
sample_buffer_size,
sampling_interval_secs,
min_sma_rate_per_sec,
}
}
pub fn update(&mut self, value: f64) -> bool {
let elapsed = self.last_instant.elapsed();
let delta = value - self.last_value;
if elapsed.as_secs_f64() >= self.sampling_interval_secs {
let count = (elapsed.as_secs_f64() / self.sampling_interval_secs) as usize;
let mut total_pushed = 0.0;
let mut total_popped = 0.0;
for _ in 0..count {
let pushed = delta.max(0.0) / count as f64;
self.sample_buffer.push_back(delta.max(0.0) / count as f64);
total_pushed += pushed;
if self.sample_buffer.len() > self.sample_buffer_size {
total_popped += self.sample_buffer.pop_front().unwrap_or_default();
}
}
if let Some(sma_rate_per_sec) = self.maybe_sma_rate_per_sec {
self.maybe_sma_rate_per_sec = Some(
sma_rate_per_sec
+ (total_pushed - total_popped) / self.sample_buffer_size as f64,
)
} else if self.sample_buffer.len() >= self.sample_buffer_size {
self.maybe_sma_rate_per_sec =
Some(self.sample_buffer.iter().sum::<f64>() / self.sample_buffer_size as f64)
}
self.last_instant = Instant::now();
self.last_value = value;
}
if let Some(sma_rate_per_sec) = self.maybe_sma_rate_per_sec {
sma_rate_per_sec >= self.min_sma_rate_per_sec
} else {
true
}
}
} }