use std::time::Duration;
use crate::{Length, Measurement};
#[derive(PartialEq, PartialOrd, Clone, Copy, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Speed {
mps: f64,
}
impl Speed {
const KPH_TO_MPS: f64 = 1_000.0 / 3_600.0;
const KNOTS_TO_MPS: f64 = 1_852.0 / 3_600.0;
pub const ZERO: Speed = Speed { mps: 0.0 };
pub const fn from_metres_per_second(mps: f64) -> Self {
Speed { mps }
}
pub fn from_kilometres_per_hour(kph: f64) -> Self {
Speed::from_metres_per_second(kph * Self::KPH_TO_MPS)
}
pub fn from_knots(knots: f64) -> Self {
Speed::from_metres_per_second(knots * Self::KNOTS_TO_MPS)
}
#[inline]
pub const fn as_metres_per_second(&self) -> f64 {
self.mps
}
pub fn as_kilometres_per_hour(&self) -> f64 {
self.mps / Self::KPH_TO_MPS
}
pub fn as_knots(&self) -> f64 {
self.mps / Self::KNOTS_TO_MPS
}
}
impl Measurement for Speed {
fn from_default_unit(amount: f64) -> Self {
Speed::from_metres_per_second(amount)
}
#[inline]
fn as_default_unit(&self) -> f64 {
self.mps
}
}
impl_measurement! { Speed }
impl ::std::ops::Div<Duration> for Length {
type Output = Speed;
fn div(self, rhs: Duration) -> Speed {
let mps = self.as_metres() / rhs.as_secs_f64();
Speed::from_metres_per_second(mps)
}
}
impl ::std::ops::Mul<Duration> for Speed {
type Output = Length;
fn mul(self, rhs: Duration) -> Length {
let metres = self.as_metres_per_second() * rhs.as_secs_f64();
Length::from_metres(metres)
}
}
#[cfg(feature = "uom")]
impl From<uom::si::f64::Velocity> for Speed {
fn from(value: uom::si::f64::Velocity) -> Self {
Self::from_metres_per_second(value.get::<uom::si::velocity::meter_per_second>())
}
}
#[cfg(feature = "uom")]
impl From<Speed> for uom::si::f64::Velocity {
fn from(value: Speed) -> Self {
Self::new::<uom::si::velocity::meter_per_second>(value.as_metres_per_second())
}
}
#[cfg(test)]
mod tests {
use crate::{Length, Speed};
use std::time::Duration;
#[test]
fn conversions() {
assert_eq!(1.852, Speed::from_knots(1.0).as_kilometres_per_hour());
assert_eq_e6(0.514444, Speed::from_knots(1.0).as_metres_per_second());
assert_eq_e6(
0.277778,
Speed::from_kilometres_per_hour(1.0).as_metres_per_second(),
);
assert_eq_e6(0.539957, Speed::from_kilometres_per_hour(1.0).as_knots());
assert_eq_e6(
3.6,
Speed::from_metres_per_second(1.0).as_kilometres_per_hour(),
);
assert_eq_e6(1.943844, Speed::from_metres_per_second(1.0).as_knots());
fn assert_eq_e6(expected: f64, actual: f64) {
let d = (expected - actual).abs();
assert!(d < 1e-6, "expected {} but was {}", expected, actual);
}
}
#[test]
fn std_ops() {
assert_eq!(
Speed::from_metres_per_second(2.0),
2.0 * Speed::from_metres_per_second(1.0)
);
assert_eq!(
Speed::from_metres_per_second(2.0),
Speed::from_metres_per_second(1.0) + Speed::from_metres_per_second(1.0)
);
assert_eq!(
Speed::from_metres_per_second(0.0),
Speed::from_metres_per_second(1.0) - Speed::from_metres_per_second(1.0)
);
assert_eq!(
Speed::from_metres_per_second(1.0),
Length::from_metres(1.0) / Duration::from_secs(1)
);
assert_eq!(
Length::from_metres(1.0),
Speed::from_metres_per_second(1.0) * Duration::from_secs(1)
);
}
#[cfg(feature = "uom")]
#[test]
fn uom() {
let speed = Speed::from_metres_per_second(1.0);
let uom = uom::si::f64::Velocity::from(speed);
let roundtrip = Speed::from(uom);
assert_eq!(speed, roundtrip);
}
}