use embedded_flight_core::filter::LowPassFilter;
use num_traits::Float;
pub struct SlewLimiter<const N: usize> {
slew_rate_max: f32,
slew_rate_tau: f32,
slew_filter: LowPassFilter<f32>,
pub output_slew_rate: f32,
_modifier_slew_rate: f32,
last_sample: f32,
_max_pos_slew_rate: f32,
_max_neg_slew_rate: f32,
_max_pos_slew_event_ms: u32,
_max_neg_slew_event_ms: u32,
_pos_event_index: u8,
_neg_event_index: u8,
_pos_event_ms: [u32; N],
_neg_event_ms: [u32; N],
_pos_event_stored: bool,
_neg_event_stored: bool,
window_ms: u32,
modifier_gain: f32,
cutoff_freq: f32,
}
impl<const N: usize> SlewLimiter<N> {
pub fn new(slew_rate_max: f32, slew_rate_tau: f32) -> Self {
Self {
slew_rate_max,
slew_rate_tau,
slew_filter: LowPassFilter::default(),
output_slew_rate: 0.,
_modifier_slew_rate: 0.,
last_sample: 0.,
_max_pos_slew_rate: 0.,
_max_neg_slew_rate: 0.,
_max_pos_slew_event_ms: 0,
_max_neg_slew_event_ms: 0,
_pos_event_index: 0,
_neg_event_index: 0,
_pos_event_ms: [0; N],
_neg_event_ms: [0; N],
_pos_event_stored: false,
_neg_event_stored: false,
window_ms: 300,
modifier_gain: 1.5,
cutoff_freq: 25.,
}
}
pub fn modifier(&mut self, sample: f32, dt: f32, now_ms: u32) -> f32 {
if self.slew_rate_max <= 0. {
self.output_slew_rate = 0.;
return 1.;
}
let slew_rate =
self.slew_filter
.filter((sample - self.last_sample) / dt, self.cutoff_freq, dt);
self.last_sample = sample;
let decay_alpha = dt.min(self.slew_rate_tau) / self.slew_rate_tau;
if !self._pos_event_stored && slew_rate > self.slew_rate_max {
if self._pos_event_index as usize >= N {
self._pos_event_index = 0;
}
self._pos_event_ms[self._pos_event_index as usize] = now_ms;
self._pos_event_index += 1;
self._pos_event_stored = true;
self._neg_event_stored = false;
}
if !self._neg_event_stored && slew_rate < -self.slew_rate_max {
if self._neg_event_index as usize >= N {
self._neg_event_index = 0;
}
self._neg_event_ms[self._neg_event_index as usize] = now_ms;
self._neg_event_index += 1;
self._neg_event_stored = true;
self._pos_event_stored = false;
}
let mut oldest_ms = now_ms;
for index in 0..N {
if self._pos_event_ms[index] < oldest_ms {
oldest_ms = self._pos_event_ms[index];
}
if self._neg_event_ms[index] < oldest_ms {
oldest_ms = self._neg_event_ms[index];
}
}
if slew_rate > self._max_pos_slew_rate {
self._max_pos_slew_rate = slew_rate.min(10. * self.slew_rate_max);
self._max_pos_slew_event_ms = now_ms;
} else if now_ms - self._max_pos_slew_event_ms > self.window_ms {
self._max_pos_slew_rate *= 1. - decay_alpha;
}
if slew_rate < -self._max_neg_slew_rate {
self._max_neg_slew_rate = -slew_rate.min(10. * self.slew_rate_max);
self._max_neg_slew_event_ms = now_ms;
} else if now_ms - self._max_neg_slew_event_ms > self.window_ms {
self._max_neg_slew_rate *= 1. - decay_alpha;
}
let raw_slew_rate = 0.5 * (self._max_pos_slew_rate + self._max_neg_slew_rate);
let mut modifier_input = raw_slew_rate;
if (now_ms - oldest_ms) as usize > (N + 1) * self.window_ms as usize {
let oldest_time_from_window = 0.001
* ((now_ms - oldest_ms) as f32 - ((N + 1) * self.window_ms as usize) as f32) as f32;
modifier_input *= (-oldest_time_from_window / self.slew_rate_tau).exp();
}
let attack_alpha = (2. * decay_alpha).min(1.);
self._modifier_slew_rate =
(1. - attack_alpha) * self._modifier_slew_rate + attack_alpha * modifier_input;
self._modifier_slew_rate = self._modifier_slew_rate.min(modifier_input);
self.output_slew_rate =
(1. - attack_alpha) * self.output_slew_rate + attack_alpha * raw_slew_rate;
self.output_slew_rate = self.output_slew_rate.min(raw_slew_rate);
if self._modifier_slew_rate > self.slew_rate_max {
self.slew_rate_max
/ (self.slew_rate_max
+ self.modifier_gain * (self._modifier_slew_rate - self.slew_rate_max))
} else {
1.
}
}
}