#![cfg(all(feature = "std", feature = "fixed"))] use std::assert_eq; use fixed::{ traits::{Fixed, ToFixed}, types::I32F32, }; use proptest::prelude::*; use guv::std::fixed::calculate_h; use guv::Number; mod strategies; // // property-based tests // proptest! { // // calculate_h tests // #[cfg(feature = "fixed")] #[test] fn calculate_h_returns_number_of_seconds_elapsed_fixed64( (before, after) in strategies::ordered_system_times() ) { let h_calc = calculate_h::(after, before) .expect("calculate_h should succeed when measurement_time happens after last_update_time"); let h: I32F32 = after.duration_since(before) .expect("before should come before after") .as_secs_f64() .to_fixed(); let abs_diff = (h - h_calc).abs(); assert!( abs_diff <= 1000 * I32F32::DELTA, "delta: {:?}, h_calc: {:?}, h: {:?}, abs_diff: {:?}", I32F32::DELTA, h_calc, h, abs_diff ) } #[cfg(feature = "fixed")] #[test] fn calculate_h_returns_err_when_measurement_time_before_last_update_time_fixed64( (before, after) in strategies::ordered_system_times() ) { calculate_h::(before, after) .expect_err("calculate_h should fail when measurement_time happens before last_update_time"); } // // PidController tests // #[cfg(feature = "fixed")] #[test] fn trivial_update_should_not_fail_fixed64( pid_controller in strategies::default_pid_controllers::() ) { let mut pid_controller = pid_controller .expect("constructor should not error"); let result = pid_controller.update( I32F32::ZERO, I32F32::ZERO, I32F32::checked_from_num(0.1) .expect("0.1 must be representable as I32F32"), I32F32::ZERO, I32F32::ZERO ); assert_eq!(result, I32F32::ZERO); assert_eq!(pid_controller.control_output(), 0.0); } #[cfg(feature = "fixed")] #[test] fn in_place_constants_update_should_not_fail_fixed64( proportional_gain in strategies::bounded_numbers( ::zero(), ::max_value() ), integral_time_constant in strategies::bounded_numbers( ::epsilon(), ::max_value() ), derivative_time_constant in strategies::bounded_numbers( ::epsilon(), ::max_value() ), set_point_coefficient in strategies::bounded_numbers( ::zero(), ::max_value() ), pid_controller in strategies::default_pid_controllers::(), ) { let mut pid_controller = pid_controller .expect("constructor should not error"); let zero = ::zero(); assert_eq!( pid_controller .update(zero, zero, I32F32::from_num(0.001), zero, zero), zero ); pid_controller .update_constants_mut( proportional_gain, integral_time_constant, derivative_time_constant, set_point_coefficient, ) .expect("updating constants should not fail"); assert_eq!( pid_controller .update(zero, zero, I32F32::from_num(0.001), zero, zero), zero ); assert_eq!(pid_controller.control_output(), zero); } #[cfg(feature = "fixed")] #[test] fn copy_constants_update_should_not_fail_fixed64( proportional_gain in strategies::bounded_numbers( ::zero(), ::max_value() ), integral_time_constant in strategies::bounded_numbers( ::epsilon(), ::max_value() ), derivative_time_constant in strategies::bounded_numbers( ::epsilon(), ::max_value() ), set_point_coefficient in strategies::bounded_numbers( ::zero(), ::max_value() ), pid_controller in strategies::default_pid_controllers::(), ) { let mut pid_controller = pid_controller .expect("constructor should not error"); let zero = ::zero(); assert_eq!( pid_controller .update(zero, zero, I32F32::from_num(0.001), zero, zero), zero ); let mut another_pid_controller = pid_controller .update_constants( proportional_gain, integral_time_constant, derivative_time_constant, set_point_coefficient, ) .expect("updating constants should not fail"); let one = ::one(); let eps = ::epsilon(); let one_k = I32F32::from_num(1000); another_pid_controller .update(one_k, one_k, eps, -one, one); assert_eq!(pid_controller.control_output(), zero); } } // // visual tests // #[cfg(feature = "fixed")] fn process(u: T, y: T, t: T) -> T { let two = T::one().safe_add(T::one()); // -0.105 * y * t + 0.105 * u * (t + 2.0) + (2.0 * T::pi() * t).cos() ::from_num(-0.105) .safe_mul(y) .safe_mul(t) .safe_add( ::from_num(0.105) .safe_mul(u) .safe_mul(t.safe_add(two)), ) .safe_add(two.safe_mul(T::pi()).safe_mul(t).cos()) } #[cfg(feature = "fixed")] #[test] fn test_process() { let result = process::(I32F32::ZERO, I32F32::ZERO, I32F32::ZERO); let abs_diff = (I32F32::ONE - result).abs(); assert!( abs_diff <= 2 * I32F32::DELTA, "delta: {:?}, result: {:?}, abs_diff: {:?}", I32F32::DELTA, result, abs_diff ); }