#[cfg(feature = "simulation")]
mod fixtures;
#[cfg(feature = "simulation")]
mod data;
#[cfg(feature = "simulation")]
mod test_pid_numerical_performance {
use super::data::*;
use super::fixtures::test_pid::*;
use core::time::Duration;
use discrete_pid::pid::*;
use discrete_pid::sim;
use discrete_pid::time::Millis;
use approx::assert_relative_eq;
use nalgebra as na;
fn configure_pid_nondefault(pid: &mut FuncPidController<f64>) {
assert!(pid.config_mut().set_kp(10.0).is_ok());
assert!(pid.config_mut().set_ki(20.0).is_ok());
assert!(pid.config_mut().set_kd(1.0).is_ok());
assert!(pid.config_mut().set_filter_tc(0.02).is_ok());
pid.config_mut().set_use_strict_causal_integrator(true);
}
#[test]
fn test_simulink_open_loop_behavior_compliance() {
let (mut pid, mut ctx) = make_controller();
configure_pid_nondefault(&mut pid);
assert!(pid.config().use_strict_causal_integrator());
let mut output: f64;
let sine = sim::SignalGenerator::new(sim::WaveForm::Sine, Millis(0), 1.0, 0.0);
for i in 0..1000usize {
let last_time = ctx.last_time().unwrap_or(Millis(0));
let timestamp = last_time + Duration::from_millis(FIXED_STEP_SIZE_MS);
let sine_signal = sine.generate(last_time);
(output, ctx) = pid.compute(ctx, 0.0, sine_signal, timestamp, None);
if i % DOWNSAMPLE_FACTOR == 0 {
let expected = OL_SINE_RESPONSE[i / DOWNSAMPLE_FACTOR];
assert_relative_eq!(output, expected, epsilon = 1e-12);
}
}
}
#[test]
fn test_simulink_closed_loop_behavior_compliance() {
let (mut pid, mut ctx) = make_controller();
configure_pid_nondefault(&mut pid);
assert!(pid.config().use_strict_causal_integrator());
let mut state = na::vector![0.0, 0.0];
let mut control: f64;
let mut output: f64 = 0.0;
let mdl = sim::MassSpringDamper {
natural_frequency: 2.0 * std::f64::consts::PI,
damping_ratio: 0.2,
};
let sine = sim::SignalGenerator::new(sim::WaveForm::Sine, Millis(0), 1.0, 0.0);
const FIXED_STEP_SIZE_S: f64 = FIXED_STEP_SIZE_MS as f64 * 0.001;
for i in 0..1000usize {
let last_time = ctx.last_time().unwrap_or(Millis(0));
let timestamp = last_time + Duration::from_millis(FIXED_STEP_SIZE_MS);
(control, ctx) = pid.compute(ctx, output, sine.generate(last_time), timestamp, None);
if i % DOWNSAMPLE_FACTOR == 0 {
let expected = CL_SINE_RESPONSE[i / DOWNSAMPLE_FACTOR];
assert_relative_eq!(output, expected, epsilon = 1e-12);
}
state += mdl.f(state, control) * FIXED_STEP_SIZE_S;
output = mdl.h(state);
}
}
#[test]
fn test_forwarding_to_stateful_pid_open_loop_numerical_equivalence() {
let (mut func_pid, mut ctx) = make_controller();
let mut stateful_pid = make_stateful_controller();
assert!(func_pid.config_mut().set_kd(0.01).is_ok());
assert!(func_pid.config_mut().set_filter_tc(0.02).is_ok());
assert!(stateful_pid.config_mut().set_kd(0.01).is_ok());
assert!(stateful_pid.config_mut().set_filter_tc(0.02).is_ok());
let mut expected: f64;
for _ in 0..1000usize {
let last_time = ctx.last_time().unwrap_or(Millis(0));
let timestamp = last_time + Duration::from_millis(FIXED_STEP_SIZE_MS);
let sine_signal = sim::sine_signal(last_time, Millis(0));
(expected, ctx) = func_pid.compute(ctx, 0.0, sine_signal, timestamp, None);
let result = stateful_pid.compute(0.0, sine_signal, timestamp, None);
assert_eq!(result, expected);
}
}
#[test]
fn test_forwarding_to_stateful_pid_closed_loop_numerical_equivalence() {
let (mut func_pid, mut ctx) = make_controller();
let mut stateful_pid = make_stateful_controller();
assert!(func_pid.config_mut().set_kd(0.01).is_ok());
assert!(func_pid.config_mut().set_filter_tc(0.02).is_ok());
assert!(stateful_pid.config_mut().set_kd(0.01).is_ok());
assert!(stateful_pid.config_mut().set_filter_tc(0.02).is_ok());
let mut state = na::vector![0.0, 0.0];
let mut expected: f64;
let mut output: f64 = 0.0;
let mdl = sim::MassSpringDamper {
natural_frequency: 2.0 * std::f64::consts::PI,
damping_ratio: 0.2,
};
const FIXED_STEP_SIZE_S: f64 = FIXED_STEP_SIZE_MS as f64 * 0.001;
for _ in 0..1000usize {
let last_time = ctx.last_time().unwrap_or(Millis(0));
let timestamp = last_time + Duration::from_millis(FIXED_STEP_SIZE_MS);
let sine_signal = sim::sine_signal(last_time, Millis(0));
(expected, ctx) = func_pid.compute(ctx, output, sine_signal, timestamp, None);
let result = stateful_pid.compute(output, sine_signal, timestamp, None);
assert_eq!(result, expected);
state += mdl.f(state, expected) * FIXED_STEP_SIZE_S;
output = mdl.h(state);
}
}
}