Skip to main content

qtty_core/units/velocity/
mod.rs

1// SPDX-License-Identifier: BSD-3-Clause
2// Copyright (C) 2026 Vallés Puig, Ramon
3
4//! Velocity unit aliases (`Length / Time`).
5//!
6//! This module defines velocity units as *pure type aliases* over [`Per`] using
7//! length and time units already defined elsewhere in the crate.
8//!
9//! No standalone velocity units are introduced: every velocity is represented as
10//! `Length / Time` at the type level.
11//!
12//! ## Design notes
13//!
14//! - The velocity *dimension* is [`Velocity`](crate::Velocity) = [`Length`] / [`Time`].
15//! - All velocity units are zero-cost type aliases.
16//! - Conversions are handled automatically via the underlying length and time units.
17//! - No assumptions are made about reference frames, relativistic effects, or media.
18//!
19//! ## Examples
20//!
21//! ```rust
22//! use qtty_core::length::{Kilometer, Kilometers};
23//! use qtty_core::time::{Second, Seconds};
24//! use qtty_core::velocity::Velocity;
25//!
26//! let d = Kilometers::new(42.0);
27//! let t = Seconds::new(2.0);
28//! let v: Velocity<Kilometer, Second> = d / t;
29//! assert!((v.value() - 21.0).abs() < 1e-12);
30//! ```
31//!
32//! ```rust
33//! use qtty_core::length::{Meter, Meters};
34//! use qtty_core::time::{Hour, Hours};
35//! use qtty_core::velocity::Velocity;
36//!
37//! let v: Velocity<Meter, Hour> = Meters::new(3_600.0) / Hours::new(1.0);
38//! assert!((v.value() - 3_600.0).abs() < 1e-12);
39//! ```
40
41use crate::{Per, Quantity, Unit};
42
43/// Re-export the velocity dimension from the dimension module.
44pub use crate::dimension::Velocity as VelocityDimension;
45
46/// Marker trait for any unit whose dimension is [`Velocity`](crate::Velocity).
47pub trait VelocityUnit: Unit<Dim = crate::Velocity> {}
48impl<T: Unit<Dim = crate::Velocity>> VelocityUnit for T {}
49
50/// A velocity quantity parameterized by length and time units.
51///
52/// # Examples
53///
54/// ```rust
55/// use qtty_core::length::{Kilometer, Meter};
56/// use qtty_core::time::{Second, Hour};
57/// use qtty_core::velocity::Velocity;
58///
59/// let v1: Velocity<Meter, Second> = Velocity::new(10.0);
60/// let v2: Velocity<Kilometer, Hour> = Velocity::new(36.0);
61/// ```
62pub type Velocity<N, D> = Quantity<Per<N, D>>;
63
64#[cfg(feature = "astro")]
65pub mod astro;
66#[cfg(feature = "astro")]
67pub use astro::AU_PER_DAY_C;
68#[cfg(feature = "astro")]
69pub use astro::*;
70
71#[cfg(all(test, feature = "std"))]
72mod tests {
73    use super::*;
74    use crate::units::length::{Kilometer, Kilometers, Meter};
75    use crate::units::time::{Hour, Second, Seconds};
76    use crate::Per;
77    use approx::{assert_abs_diff_eq, assert_relative_eq};
78    use proptest::prelude::*;
79
80    // ─────────────────────────────────────────────────────────────────────────────
81    // Basic velocity conversions
82    // ─────────────────────────────────────────────────────────────────────────────
83
84    #[test]
85    fn km_per_s_to_m_per_s() {
86        let v: Velocity<Kilometer, Second> = Velocity::new(1.0);
87        let v_mps: Velocity<Meter, Second> = v.to();
88        assert_abs_diff_eq!(v_mps.value(), 1000.0, epsilon = 1e-9);
89    }
90
91    #[test]
92    fn m_per_s_to_km_per_s() {
93        let v: Velocity<Meter, Second> = Velocity::new(1000.0);
94        let v_kps: Velocity<Kilometer, Second> = v.to();
95        assert_abs_diff_eq!(v_kps.value(), 1.0, epsilon = 1e-12);
96    }
97
98    #[test]
99    fn km_per_h_to_m_per_s() {
100        let v: Velocity<Kilometer, Hour> = Velocity::new(3.6);
101        let v_mps: Velocity<Meter, Second> = v.to();
102        // 3.6 km/h = 1 m/s
103        assert_abs_diff_eq!(v_mps.value(), 1.0, epsilon = 1e-12);
104    }
105
106    #[test]
107    fn km_per_h_to_km_per_s() {
108        let v: Velocity<Kilometer, Hour> = Velocity::new(3600.0);
109        let v_kps: Velocity<Kilometer, Second> = v.to();
110        // 3600 km/h = 1 km/s
111        assert_abs_diff_eq!(v_kps.value(), 1.0, epsilon = 1e-12);
112    }
113
114    // ─────────────────────────────────────────────────────────────────────────────
115    // Per ratio behavior
116    // ─────────────────────────────────────────────────────────────────────────────
117
118    #[test]
119    fn per_ratio_km_s() {
120        // Per<Kilometer, Second> should have RATIO = 1000 / 1 = 1000
121        let ratio = <Per<Kilometer, Second>>::RATIO;
122        // Kilometer::RATIO = 1000, Second::RATIO = 1.0
123        // So Per ratio = 1000 / 1.0 = 1000
124        assert_relative_eq!(ratio, 1000.0, max_relative = 1e-12);
125    }
126
127    #[test]
128    fn per_ratio_m_s() {
129        // Per<Meter, Second> has RATIO = 1 / 1 = 1
130        let ratio = <Per<Meter, Second>>::RATIO;
131        assert_relative_eq!(ratio, 1.0, max_relative = 1e-12);
132    }
133
134    // ─────────────────────────────────────────────────────────────────────────────
135    // Velocity * Time = Length
136    // ─────────────────────────────────────────────────────────────────────────────
137
138    #[test]
139    fn velocity_times_time() {
140        let v: Velocity<Kilometer, Second> = Velocity::new(10.0);
141        let t: Seconds = Seconds::new(5.0);
142        let d: Kilometers = (v * t).to();
143        assert_abs_diff_eq!(d.value(), 50.0, epsilon = 1e-9);
144    }
145
146    #[test]
147    fn time_times_velocity() {
148        let v: Velocity<Kilometer, Second> = Velocity::new(10.0);
149        let t: Seconds = Seconds::new(5.0);
150        let d: Kilometers = (t * v).to();
151        assert_abs_diff_eq!(d.value(), 50.0, epsilon = 1e-9);
152    }
153
154    // ─────────────────────────────────────────────────────────────────────────────
155    // Length / Time = Velocity
156    // ─────────────────────────────────────────────────────────────────────────────
157
158    #[test]
159    fn length_div_time() {
160        let d: Kilometers = Kilometers::new(100.0);
161        let t: Seconds = Seconds::new(10.0);
162        let v: Velocity<Kilometer, Second> = d / t;
163        assert_abs_diff_eq!(v.value(), 10.0, epsilon = 1e-9);
164    }
165
166    // ─────────────────────────────────────────────────────────────────────────────
167    // Roundtrip conversions
168    // ─────────────────────────────────────────────────────────────────────────────
169
170    #[test]
171    fn roundtrip_mps_kps() {
172        let original: Velocity<Meter, Second> = Velocity::new(500.0);
173        let converted: Velocity<Kilometer, Second> = original.to();
174        let back: Velocity<Meter, Second> = converted.to();
175        assert_abs_diff_eq!(back.value(), original.value(), epsilon = 1e-9);
176    }
177
178    // ─────────────────────────────────────────────────────────────────────────────
179    // Property-based tests
180    // ─────────────────────────────────────────────────────────────────────────────
181
182    proptest! {
183        #[test]
184        fn prop_roundtrip_mps_kps(v in 1e-6..1e6f64) {
185            let original: Velocity<Meter, Second> = Velocity::new(v);
186            let converted: Velocity<Kilometer, Second> = original.to();
187            let back: Velocity<Meter, Second> = converted.to();
188            prop_assert!((back.value() - original.value()).abs() < 1e-9 * v.abs().max(1.0));
189        }
190
191        #[test]
192        fn prop_mps_kps_ratio(v in 1e-6..1e6f64) {
193            let mps: Velocity<Meter, Second> = Velocity::new(v);
194            let kps: Velocity<Kilometer, Second> = mps.to();
195            // 1000 m/s = 1 km/s
196            prop_assert!((mps.value() / kps.value() - 1000.0).abs() < 1e-9);
197        }
198
199        #[test]
200        fn prop_velocity_time_roundtrip(
201            v_val in 1e-3..1e3f64,
202            t_val in 1e-3..1e3f64
203        ) {
204            let v: Velocity<Kilometer, Second> = Velocity::new(v_val);
205            let t: Seconds = Seconds::new(t_val);
206            let d: Kilometers = (v * t).to();
207            // d / t should give back v
208            let v_back: Velocity<Kilometer, Second> = d / t;
209            prop_assert!((v_back.value() - v.value()).abs() / v.value() < 1e-12);
210        }
211    }
212}