Skip to main content

space_units/quantities/
mechanics.rs

1use super::DisplayWithUnit;
2
3// Length constants
4const METERS_PER_FOOT: f64 = 0.3048;
5const METERS_PER_NAUTICAL_MILE: f64 = 1852.0;
6const METERS_PER_MILE: f64 = 1609.344;
7const METERS_PER_AU: f64 = 149_597_870_700.0;
8const METERS_PER_PARSEC: f64 = 3.085_677_581_491_367e16;
9const METERS_PER_LIGHT_YEAR: f64 = 9.460_730_472_580_8e15;
10const METERS_PER_EARTH_RADIUS: f64 = 6_371_000.0;
11const METERS_PER_SOLAR_RADIUS: f64 = 6.957e8;
12
13// Time constants
14const SECONDS_PER_MINUTE: f64 = 60.0;
15const SECONDS_PER_HOUR: f64 = 3_600.0;
16const SECONDS_PER_DAY: f64 = 86_400.0;
17const SECONDS_PER_JULIAN_YEAR: f64 = 365.25 * SECONDS_PER_DAY;
18
19// Mass constants
20pub(super) const KG_PER_POUND: f64 = 0.453_592_37;
21const KG_PER_SLUG: f64 = 14.593_903;
22const KG_PER_SOLAR_MASS: f64 = 1.988_92e30;
23const KG_PER_EARTH_MASS: f64 = 5.972_2e24;
24
25/// Standard gravity (m/s^2).
26pub(super) const G0_MPS2: f64 = 9.806_65;
27
28// Force constants
29const NEWTONS_PER_LBF: f64 = 4.448_221_615_260_5;
30
31// -------------------------
32// Length
33// -------------------------
34
35/// A length / distance quantity, stored canonically in **meters** (m).
36///
37/// # Construction
38/// ```
39/// use space_units::prelude::*;
40///
41/// let a = Length::from_km(384_400.0);
42/// let b = 384_400.0.km();        // NumericExt shorthand
43/// ```
44///
45/// # Supported units
46///
47/// | Constructor         | Accessor          | Unit                  |
48/// |---------------------|-------------------|-----------------------|
49/// | `from_m`            | `in_m`            | meter (m)             |
50/// | `from_km`           | `in_km`           | kilometer (km)        |
51/// | `from_mm`           | `in_mm`           | millimeter (mm)       |
52/// | `from_cm`           | `in_cm`           | centimeter (cm)       |
53/// | `from_um`           | `in_um`           | micrometer (um)       |
54/// | `from_nm`           | `in_nm`           | nanometer (nm)        |
55/// | `from_au`           | `in_au`           | astronomical unit (AU)|
56/// | `from_pc`           | `in_pc`           | parsec (pc)           |
57/// | `from_ly`           | `in_ly`           | light-year (ly)       |
58/// | `from_ft`           | `in_ft`           | foot (ft)             |
59/// | `from_nmi`          | `in_nmi`          | nautical mile (nmi)   |
60/// | `from_mi`           | `in_mi`           | statute mile (mi)     |
61/// | `from_earth_radii`  | `in_earth_radii`  | Earth radius          |
62/// | `from_solar_radii`  | `in_solar_radii`  | Solar radius          |
63///
64/// # Typed arithmetic
65///
66/// - [`Length`] / [`Time`] → [`Velocity`]
67/// - [`Length`] / [`Velocity`] → [`Time`]
68/// - [`Length`] × [`Length`] → [`Area`](crate::Area)
69/// - [`Length`] × [`Velocity`] → [`SpecificAngularMomentum`](crate::SpecificAngularMomentum)
70/// - [`Length`] × [`Force`] → [`Energy`](crate::Energy)
71/// - [`Length`] × [`Area`](crate::Area) → [`Volume`](crate::Volume)
72#[must_use]
73#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
74pub struct Length(pub(crate) f64);
75
76/// Display / conversion units for [`Length`].
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub enum LengthUnit {
79    /// Meter (m) -- SI base unit.
80    Meter,
81    /// Millimeter (mm).
82    Millimeter,
83    /// Centimeter (cm).
84    Centimeter,
85    /// Kilometer (km).
86    Kilometer,
87    /// Micrometer (um).
88    Micrometer,
89    /// Nanometer (nm).
90    Nanometer,
91    /// Astronomical unit (AU) -- ~149.6 Gm.
92    AstronomicalUnit,
93    /// Parsec (pc) -- ~3.086e16 m.
94    Parsec,
95    /// Light-year (ly) -- ~9.461e15 m.
96    LightYear,
97    /// International foot (ft) -- 0.3048 m.
98    Foot,
99    /// Nautical mile (nmi) -- 1852 m.
100    NauticalMile,
101    /// Statute mile (mi) -- 1609.344 m.
102    Mile,
103    /// Mean Earth radius -- 6 371 000 m.
104    EarthRadius,
105    /// Solar radius -- 6.957e8 m.
106    SolarRadius,
107}
108
109impl LengthUnit {
110    const fn symbol(self) -> &'static str {
111        match self {
112            Self::Meter => "m",
113            Self::Millimeter => "mm",
114            Self::Centimeter => "cm",
115            Self::Kilometer => "km",
116            Self::Micrometer => "um",
117            Self::Nanometer => "nm",
118            Self::AstronomicalUnit => "AU",
119            Self::Parsec => "pc",
120            Self::LightYear => "ly",
121            Self::Foot => "ft",
122            Self::NauticalMile => "nmi",
123            Self::Mile => "mi",
124            Self::EarthRadius => "R_earth",
125            Self::SolarRadius => "R_sun",
126        }
127    }
128
129    const fn meters_per_unit(self) -> f64 {
130        match self {
131            Self::Meter => 1.0,
132            Self::Millimeter => 1e-3,
133            Self::Centimeter => 1e-2,
134            Self::Kilometer => 1_000.0,
135            Self::Micrometer => 1e-6,
136            Self::Nanometer => 1e-9,
137            Self::AstronomicalUnit => METERS_PER_AU,
138            Self::Parsec => METERS_PER_PARSEC,
139            Self::LightYear => METERS_PER_LIGHT_YEAR,
140            Self::Foot => METERS_PER_FOOT,
141            Self::NauticalMile => METERS_PER_NAUTICAL_MILE,
142            Self::Mile => METERS_PER_MILE,
143            Self::EarthRadius => METERS_PER_EARTH_RADIUS,
144            Self::SolarRadius => METERS_PER_SOLAR_RADIUS,
145        }
146    }
147}
148
149impl Length {
150    /// Create from meters (canonical unit).
151    pub const fn from_m(val: f64) -> Self {
152        Self(val)
153    }
154
155    /// Create from kilometers. 1 km = 1 000 m.
156    pub const fn from_km(val: f64) -> Self {
157        Self(val * 1_000.0)
158    }
159
160    /// Create from millimeters. 1 mm = 0.001 m.
161    pub const fn from_mm(val: f64) -> Self {
162        Self(val * 1e-3)
163    }
164
165    /// Create from centimeters. 1 cm = 0.01 m.
166    pub const fn from_cm(val: f64) -> Self {
167        Self(val * 1e-2)
168    }
169
170    /// Create from micrometers. 1 um = 1e-6 m.
171    pub const fn from_um(val: f64) -> Self {
172        Self(val * 1e-6)
173    }
174
175    /// Create from nanometers. 1 nm = 1e-9 m.
176    pub const fn from_nm(val: f64) -> Self {
177        Self(val * 1e-9)
178    }
179
180    /// Create from astronomical units. 1 AU = 149 597 870 700 m.
181    pub const fn from_au(val: f64) -> Self {
182        Self(val * METERS_PER_AU)
183    }
184
185    /// Create from parsecs. 1 pc = 3.086e16 m.
186    pub const fn from_pc(val: f64) -> Self {
187        Self(val * METERS_PER_PARSEC)
188    }
189
190    /// Create from light-years. 1 ly = 9.461e15 m.
191    pub const fn from_ly(val: f64) -> Self {
192        Self(val * METERS_PER_LIGHT_YEAR)
193    }
194
195    /// Create from feet. 1 ft = 0.3048 m.
196    pub const fn from_ft(val: f64) -> Self {
197        Self(val * METERS_PER_FOOT)
198    }
199
200    /// Create from nautical miles. 1 nmi = 1 852 m.
201    pub const fn from_nmi(val: f64) -> Self {
202        Self(val * METERS_PER_NAUTICAL_MILE)
203    }
204
205    /// Create from statute miles. 1 mi = 1 609.344 m.
206    pub const fn from_mi(val: f64) -> Self {
207        Self(val * METERS_PER_MILE)
208    }
209
210    /// Create from Earth radii. 1 `R_earth` = 6 371 000 m.
211    pub const fn from_earth_radii(val: f64) -> Self {
212        Self(val * METERS_PER_EARTH_RADIUS)
213    }
214
215    /// Create from Solar radii. 1 `R_sun` = 6.957e8 m.
216    pub const fn from_solar_radii(val: f64) -> Self {
217        Self(val * METERS_PER_SOLAR_RADIUS)
218    }
219
220    /// Get value in meters.
221    pub const fn in_m(self) -> f64 {
222        self.0
223    }
224
225    /// Get value in kilometers.
226    pub const fn in_km(self) -> f64 {
227        self.0 / 1_000.0
228    }
229
230    /// Get value in millimeters.
231    pub const fn in_mm(self) -> f64 {
232        self.0 / 1e-3
233    }
234
235    /// Get value in centimeters.
236    pub const fn in_cm(self) -> f64 {
237        self.0 / 1e-2
238    }
239
240    /// Get value in micrometers.
241    pub const fn in_um(self) -> f64 {
242        self.0 / 1e-6
243    }
244
245    /// Get value in nanometers.
246    pub const fn in_nm(self) -> f64 {
247        self.0 / 1e-9
248    }
249
250    /// Get value in astronomical units.
251    pub const fn in_au(self) -> f64 {
252        self.0 / METERS_PER_AU
253    }
254
255    /// Get value in parsecs.
256    pub const fn in_pc(self) -> f64 {
257        self.0 / METERS_PER_PARSEC
258    }
259
260    /// Get value in light-years.
261    pub const fn in_ly(self) -> f64 {
262        self.0 / METERS_PER_LIGHT_YEAR
263    }
264
265    /// Get value in feet.
266    pub const fn in_ft(self) -> f64 {
267        self.0 / METERS_PER_FOOT
268    }
269
270    /// Get value in nautical miles.
271    pub const fn in_nmi(self) -> f64 {
272        self.0 / METERS_PER_NAUTICAL_MILE
273    }
274
275    /// Get value in statute miles.
276    pub const fn in_mi(self) -> f64 {
277        self.0 / METERS_PER_MILE
278    }
279
280    /// Get value in Earth radii.
281    pub const fn in_earth_radii(self) -> f64 {
282        self.0 / METERS_PER_EARTH_RADIUS
283    }
284
285    /// Get value in Solar radii.
286    pub const fn in_solar_radii(self) -> f64 {
287        self.0 / METERS_PER_SOLAR_RADIUS
288    }
289
290    /// Get value in the specified [`LengthUnit`].
291    pub fn in_unit(self, unit: LengthUnit) -> f64 {
292        self.0 / unit.meters_per_unit()
293    }
294
295    /// Return a [`DisplayWithUnit`] for formatted printing in the given unit.
296    pub fn display_as(self, unit: LengthUnit) -> DisplayWithUnit {
297        DisplayWithUnit {
298            value: self.in_unit(unit),
299            symbol: unit.symbol(),
300        }
301    }
302
303    /// Absolute value.
304    pub fn abs(self) -> Self {
305        Self(self.0.abs())
306    }
307}
308
309impl_quantity_display!(Length, "m");
310
311impl_common_ops!(Length);
312
313// -------------------------
314// Time
315// -------------------------
316
317/// A time duration quantity, stored canonically in **seconds** (s).
318///
319/// # Construction
320/// ```
321/// use space_units::prelude::*;
322///
323/// let t = Time::from_hours(2.0);
324/// let t2 = 2.0.hours();          // NumericExt shorthand
325/// ```
326///
327/// # Supported units
328///
329/// | Constructor          | Accessor          | Unit              |
330/// |----------------------|-------------------|-------------------|
331/// | `from_s`             | `in_s`            | second (s)        |
332/// | `from_ms`            | `in_ms`           | millisecond (ms)  |
333/// | `from_us`            | `in_us`           | microsecond (us)  |
334/// | `from_ns`            | `in_ns`           | nanosecond (ns)   |
335/// | `from_min`           | `in_min`          | minute (min)      |
336/// | `from_hours`         | `in_hours`        | hour (h)          |
337/// | `from_days`          | `in_days`         | day (d)           |
338/// | `from_julian_years`  | `in_julian_years` | Julian year       |
339///
340/// # Typed arithmetic
341///
342/// - [`Velocity`] × [`Time`] → [`Length`]
343/// - [`Acceleration`] × [`Time`] → [`Velocity`]
344/// - [`Force`] × [`Time`] → [`Impulse`]
345/// - [`Angle`](crate::Angle) / [`Time`] → [`AngularVelocity`](crate::AngularVelocity)
346/// - [`AngularVelocity`](crate::AngularVelocity) × [`Time`] → [`Angle`](crate::Angle)
347/// - [`Mass`] / [`Time`] → [`MassFlowRate`](crate::MassFlowRate)
348/// - [`Power`](crate::Power) × [`Time`] → [`Energy`](crate::Energy)
349/// - [`Time::frequency()`](crate::Time::frequency) → [`Frequency`](crate::Frequency)
350#[must_use]
351#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
352pub struct Time(pub(crate) f64);
353
354/// Display / conversion units for [`Time`].
355#[derive(Debug, Clone, Copy, PartialEq, Eq)]
356pub enum TimeUnit {
357    /// Second (s) -- SI base unit.
358    Second,
359    /// Millisecond (ms).
360    Millisecond,
361    /// Microsecond (us).
362    Microsecond,
363    /// Nanosecond (ns).
364    Nanosecond,
365    /// Minute (min) -- 60 s.
366    Minute,
367    /// Hour (h) -- 3 600 s.
368    Hour,
369    /// Day (d) -- 86 400 s.
370    Day,
371    /// Julian year -- 365.25 days.
372    JulianYear,
373}
374
375impl TimeUnit {
376    const fn symbol(self) -> &'static str {
377        match self {
378            Self::Second => "s",
379            Self::Millisecond => "ms",
380            Self::Microsecond => "us",
381            Self::Nanosecond => "ns",
382            Self::Minute => "min",
383            Self::Hour => "h",
384            Self::Day => "d",
385            Self::JulianYear => "julian_yr",
386        }
387    }
388
389    const fn seconds_per_unit(self) -> f64 {
390        match self {
391            Self::Second => 1.0,
392            Self::Millisecond => 1e-3,
393            Self::Microsecond => 1e-6,
394            Self::Nanosecond => 1e-9,
395            Self::Minute => SECONDS_PER_MINUTE,
396            Self::Hour => SECONDS_PER_HOUR,
397            Self::Day => SECONDS_PER_DAY,
398            Self::JulianYear => SECONDS_PER_JULIAN_YEAR,
399        }
400    }
401}
402
403impl Time {
404    /// Create from seconds (canonical unit).
405    pub const fn from_s(val: f64) -> Self {
406        Self(val)
407    }
408
409    /// Create from milliseconds. 1 ms = 0.001 s.
410    pub const fn from_ms(val: f64) -> Self {
411        Self(val * 1e-3)
412    }
413
414    /// Create from microseconds. 1 us = 1e-6 s.
415    pub const fn from_us(val: f64) -> Self {
416        Self(val * 1e-6)
417    }
418
419    /// Create from nanoseconds. 1 ns = 1e-9 s.
420    pub const fn from_ns(val: f64) -> Self {
421        Self(val * 1e-9)
422    }
423
424    /// Create from minutes. 1 min = 60 s.
425    pub const fn from_min(val: f64) -> Self {
426        Self(val * SECONDS_PER_MINUTE)
427    }
428
429    /// Create from hours. 1 h = 3 600 s.
430    pub const fn from_hours(val: f64) -> Self {
431        Self(val * SECONDS_PER_HOUR)
432    }
433
434    /// Create from days. 1 d = 86 400 s.
435    pub const fn from_days(val: f64) -> Self {
436        Self(val * SECONDS_PER_DAY)
437    }
438
439    /// Create from Julian years. 1 Julian year = 365.25 days.
440    pub const fn from_julian_years(val: f64) -> Self {
441        Self(val * SECONDS_PER_JULIAN_YEAR)
442    }
443
444    /// Get value in seconds.
445    pub const fn in_s(self) -> f64 {
446        self.0
447    }
448
449    /// Get value in seconds (alias for [`in_s`](Self::in_s)).
450    pub const fn in_seconds(self) -> f64 {
451        self.0
452    }
453
454    /// Get value in minutes.
455    pub const fn in_min(self) -> f64 {
456        self.0 / SECONDS_PER_MINUTE
457    }
458
459    /// Get value in hours.
460    pub const fn in_hours(self) -> f64 {
461        self.0 / SECONDS_PER_HOUR
462    }
463
464    /// Get value in days.
465    pub const fn in_days(self) -> f64 {
466        self.0 / SECONDS_PER_DAY
467    }
468
469    /// Get value in milliseconds.
470    pub const fn in_ms(self) -> f64 {
471        self.0 / 1e-3
472    }
473
474    /// Get value in microseconds.
475    pub const fn in_us(self) -> f64 {
476        self.0 / 1e-6
477    }
478
479    /// Get value in nanoseconds.
480    pub const fn in_ns(self) -> f64 {
481        self.0 / 1e-9
482    }
483
484    /// Get value in Julian years.
485    pub const fn in_julian_years(self) -> f64 {
486        self.0 / SECONDS_PER_JULIAN_YEAR
487    }
488
489    /// Get value in the specified [`TimeUnit`].
490    pub fn in_unit(self, unit: TimeUnit) -> f64 {
491        self.0 / unit.seconds_per_unit()
492    }
493
494    /// Return a [`DisplayWithUnit`] for formatted printing in the given unit.
495    pub fn display_as(self, unit: TimeUnit) -> DisplayWithUnit {
496        DisplayWithUnit {
497            value: self.in_unit(unit),
498            symbol: unit.symbol(),
499        }
500    }
501
502    /// Absolute value.
503    pub fn abs(self) -> Self {
504        Self(self.0.abs())
505    }
506}
507
508impl_quantity_display!(Time, "s");
509
510impl_common_ops!(Time);
511
512// -------------------------
513// Mass
514// -------------------------
515
516/// A mass quantity, stored canonically in **kilograms** (kg).
517///
518/// # Construction
519/// ```
520/// use space_units::prelude::*;
521///
522/// let m = Mass::from_kg(1000.0);
523/// let m2 = 5.0.kg();             // NumericExt shorthand
524/// ```
525///
526/// # Supported units
527///
528/// | Constructor           | Accessor            | Unit                  |
529/// |-----------------------|---------------------|-----------------------|
530/// | `from_kg`             | `in_kg`             | kilogram (kg)         |
531/// | `from_g`              | `in_g`              | gram (g)              |
532/// | `from_lb`             | `in_lb`             | pound (lb)            |
533/// | `from_tonnes`         | `in_tonnes`         | metric tonne (t)      |
534/// | `from_slug`           | `in_slug`           | slug                  |
535/// | `from_solar_masses`   | `in_solar_masses`   | Solar mass (`M_sun`)    |
536/// | `from_earth_masses`   | `in_earth_masses`   | Earth mass (`M_earth`)  |
537///
538/// # Typed arithmetic
539///
540/// - [`Mass`] × [`Acceleration`] → [`Force`]
541/// - [`Mass`] × [`Velocity`] → [`Momentum`]
542/// - [`Mass`] / [`Volume`](crate::Volume) → [`Density`](crate::Density)
543/// - [`Mass`] / [`Time`] → [`MassFlowRate`](crate::MassFlowRate)
544/// - [`Force`] / [`Mass`] → [`Acceleration`]
545/// - [`Momentum`] / [`Mass`] → [`Velocity`]
546/// - [`Energy`](crate::Energy) / [`Mass`] → [`SpecificEnergy`](crate::SpecificEnergy)
547#[must_use]
548#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
549pub struct Mass(pub(crate) f64);
550
551/// Display / conversion units for [`Mass`].
552#[derive(Debug, Clone, Copy, PartialEq, Eq)]
553pub enum MassUnit {
554    /// Kilogram (kg) -- SI base unit.
555    Kilogram,
556    /// Gram (g).
557    Gram,
558    /// Metric tonne (t) -- 1 000 kg.
559    Tonne,
560    /// Avoirdupois pound (lb) -- 0.453 592 37 kg.
561    Pound,
562    /// Slug -- ~14.594 kg.
563    Slug,
564    /// Solar mass (`M_sun`) -- ~1.989e30 kg.
565    SolarMass,
566    /// Earth mass (`M_earth`) -- ~5.972e24 kg.
567    EarthMass,
568}
569
570impl MassUnit {
571    const fn symbol(self) -> &'static str {
572        match self {
573            Self::Kilogram => "kg",
574            Self::Gram => "g",
575            Self::Tonne => "t",
576            Self::Pound => "lb",
577            Self::Slug => "slug",
578            Self::SolarMass => "M_sun",
579            Self::EarthMass => "M_earth",
580        }
581    }
582
583    const fn kilograms_per_unit(self) -> f64 {
584        match self {
585            Self::Kilogram => 1.0,
586            Self::Gram => 1e-3,
587            Self::Tonne => 1e3,
588            Self::Pound => KG_PER_POUND,
589            Self::Slug => KG_PER_SLUG,
590            Self::SolarMass => KG_PER_SOLAR_MASS,
591            Self::EarthMass => KG_PER_EARTH_MASS,
592        }
593    }
594}
595
596impl Mass {
597    /// Create from kilograms (canonical unit).
598    pub const fn from_kg(val: f64) -> Self {
599        Self(val)
600    }
601
602    /// Create from grams. 1 g = 0.001 kg.
603    pub const fn from_g(val: f64) -> Self {
604        Self(val * 1e-3)
605    }
606
607    /// Create from pounds. 1 lb = 0.453 592 37 kg.
608    pub const fn from_lb(val: f64) -> Self {
609        Self(val * KG_PER_POUND)
610    }
611
612    /// Create from metric tonnes. 1 t = 1 000 kg.
613    pub const fn from_tonnes(val: f64) -> Self {
614        Self(val * 1e3)
615    }
616
617    /// Create from slugs. 1 slug = 14.593 903 kg.
618    pub const fn from_slug(val: f64) -> Self {
619        Self(val * KG_PER_SLUG)
620    }
621
622    /// Create from Solar masses. 1 `M_sun` = 1.989e30 kg.
623    pub const fn from_solar_masses(val: f64) -> Self {
624        Self(val * KG_PER_SOLAR_MASS)
625    }
626
627    /// Create from Earth masses. 1 `M_earth` = 5.972e24 kg.
628    pub const fn from_earth_masses(val: f64) -> Self {
629        Self(val * KG_PER_EARTH_MASS)
630    }
631
632    /// Get value in kilograms.
633    pub const fn in_kg(self) -> f64 {
634        self.0
635    }
636
637    /// Get value in grams.
638    pub const fn in_g(self) -> f64 {
639        self.0 / 1e-3
640    }
641
642    /// Get value in pounds.
643    pub const fn in_lb(self) -> f64 {
644        self.0 / KG_PER_POUND
645    }
646
647    /// Get value in metric tonnes.
648    pub const fn in_tonnes(self) -> f64 {
649        self.0 / 1e3
650    }
651
652    /// Get value in slugs.
653    pub const fn in_slug(self) -> f64 {
654        self.0 / KG_PER_SLUG
655    }
656
657    /// Get value in Solar masses.
658    pub const fn in_solar_masses(self) -> f64 {
659        self.0 / KG_PER_SOLAR_MASS
660    }
661
662    /// Get value in Earth masses.
663    pub const fn in_earth_masses(self) -> f64 {
664        self.0 / KG_PER_EARTH_MASS
665    }
666
667    /// Get value in the specified [`MassUnit`].
668    pub fn in_unit(self, unit: MassUnit) -> f64 {
669        self.0 / unit.kilograms_per_unit()
670    }
671
672    /// Return a [`DisplayWithUnit`] for formatted printing in the given unit.
673    pub fn display_as(self, unit: MassUnit) -> DisplayWithUnit {
674        DisplayWithUnit {
675            value: self.in_unit(unit),
676            symbol: unit.symbol(),
677        }
678    }
679
680    /// Absolute value.
681    pub fn abs(self) -> Self {
682        Self(self.0.abs())
683    }
684}
685
686impl_quantity_display!(Mass, "kg");
687
688impl_common_ops!(Mass);
689
690// -------------------------
691// Velocity
692// -------------------------
693
694/// A velocity quantity, stored canonically in **meters per second** (m/s).
695///
696/// # Construction
697/// ```
698/// use space_units::prelude::*;
699///
700/// let v = Velocity::from_kmps(7.8);
701/// let v2 = 7.8.kmps();            // NumericExt shorthand
702/// ```
703///
704/// # Supported units
705///
706/// | Constructor        | Accessor         | Unit                      |
707/// |--------------------|------------------|---------------------------|
708/// | `from_mps`         | `in_mps`         | meter/second (m/s)        |
709/// | `from_kmps`        | `in_kmps`        | kilometer/second (km/s)   |
710/// | `from_kmph`        | `in_kmph`        | kilometer/hour (km/h)     |
711/// | `from_kn`          | `in_kn`          | knot (kn)                 |
712/// | `from_au_per_day`  | `in_au_per_day`  | AU per day (AU/d)         |
713/// | `from_ftps`        | `in_ftps`        | foot/second (ft/s)        |
714///
715/// # Typed arithmetic
716///
717/// - [`Velocity`] × [`Time`] → [`Length`]
718/// - [`Velocity`] / [`Time`] → [`Acceleration`]
719/// - [`Velocity`] × [`Mass`] → [`Momentum`]
720/// - [`Velocity`] × [`Length`] → [`SpecificAngularMomentum`](crate::SpecificAngularMomentum)
721/// - [`Velocity`] × [`Velocity`] → [`SpecificEnergy`](crate::SpecificEnergy)
722/// - [`Velocity`] × [`MassFlowRate`](crate::MassFlowRate) → [`Force`]
723#[must_use]
724#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
725pub struct Velocity(pub(crate) f64);
726
727/// Display / conversion units for [`Velocity`].
728#[derive(Debug, Clone, Copy, PartialEq, Eq)]
729pub enum VelocityUnit {
730    /// Meter per second (m/s) -- SI derived.
731    MeterPerSecond,
732    /// Kilometer per second (km/s).
733    KilometerPerSecond,
734    /// Kilometer per hour (km/h).
735    KilometerPerHour,
736    /// Knot (kn) -- 0.514 444 m/s.
737    Knot,
738    /// Astronomical unit per day (AU/d).
739    AuPerDay,
740    /// Foot per second (ft/s).
741    FootPerSecond,
742}
743
744impl VelocityUnit {
745    const fn symbol(self) -> &'static str {
746        match self {
747            Self::MeterPerSecond => "m/s",
748            Self::KilometerPerSecond => "km/s",
749            Self::KilometerPerHour => "km/h",
750            Self::Knot => "kn",
751            Self::AuPerDay => "AU/d",
752            Self::FootPerSecond => "ft/s",
753        }
754    }
755
756    const fn mps_per_unit(self) -> f64 {
757        match self {
758            Self::MeterPerSecond => 1.0,
759            Self::KilometerPerSecond => 1_000.0,
760            Self::KilometerPerHour => 1_000.0 / 3_600.0,
761            Self::Knot => 0.514_444,
762            Self::AuPerDay => METERS_PER_AU / SECONDS_PER_DAY,
763            Self::FootPerSecond => METERS_PER_FOOT,
764        }
765    }
766}
767
768impl Velocity {
769    /// Create from meters per second (canonical unit).
770    pub const fn from_mps(val: f64) -> Self {
771        Self(val)
772    }
773
774    /// Create from kilometers per second. 1 km/s = 1 000 m/s.
775    pub const fn from_kmps(val: f64) -> Self {
776        Self(val * 1_000.0)
777    }
778
779    /// Create from kilometers per hour. 1 km/h = 1/3.6 m/s.
780    pub const fn from_kmph(val: f64) -> Self {
781        Self(val * (1_000.0 / 3_600.0))
782    }
783
784    /// Create from knots. 1 kn = 0.514 444 m/s.
785    pub const fn from_kn(val: f64) -> Self {
786        Self(val * 0.514_444)
787    }
788
789    /// Create from astronomical units per day. 1 AU/d = 1 731 456.8 m/s.
790    pub const fn from_au_per_day(val: f64) -> Self {
791        Self(val * (METERS_PER_AU / SECONDS_PER_DAY))
792    }
793
794    /// Create from feet per second. 1 ft/s = 0.3048 m/s.
795    pub const fn from_ftps(val: f64) -> Self {
796        Self(val * METERS_PER_FOOT)
797    }
798
799    /// Get value in meters per second.
800    pub const fn in_mps(self) -> f64 {
801        self.0
802    }
803
804    /// Get value in kilometers per second.
805    pub const fn in_kmps(self) -> f64 {
806        self.0 / 1_000.0
807    }
808
809    /// Get value in kilometers per hour.
810    pub const fn in_kmph(self) -> f64 {
811        self.0 / (1_000.0 / 3_600.0)
812    }
813
814    /// Get value in knots.
815    pub const fn in_kn(self) -> f64 {
816        self.0 / 0.514_444
817    }
818
819    /// Get value in astronomical units per day.
820    pub const fn in_au_per_day(self) -> f64 {
821        self.0 / (METERS_PER_AU / SECONDS_PER_DAY)
822    }
823
824    /// Get value in feet per second.
825    pub const fn in_ftps(self) -> f64 {
826        self.0 / METERS_PER_FOOT
827    }
828
829    /// Get value in the specified [`VelocityUnit`].
830    pub fn in_unit(self, unit: VelocityUnit) -> f64 {
831        self.0 / unit.mps_per_unit()
832    }
833
834    /// Return a [`DisplayWithUnit`] for formatted printing in the given unit.
835    pub fn display_as(self, unit: VelocityUnit) -> DisplayWithUnit {
836        DisplayWithUnit {
837            value: self.in_unit(unit),
838            symbol: unit.symbol(),
839        }
840    }
841
842    /// Absolute value.
843    pub fn abs(self) -> Self {
844        Self(self.0.abs())
845    }
846}
847
848impl_quantity_display!(Velocity, "m/s");
849
850impl_common_ops!(Velocity);
851
852// -------------------------
853// Acceleration
854// -------------------------
855
856/// An acceleration quantity, stored canonically in **meters per second squared** (m/s^2).
857///
858/// # Construction
859/// ```
860/// use space_units::prelude::*;
861///
862/// let a = Acceleration::from_g(1.0);
863/// let a2 = 1.0.g0();               // NumericExt shorthand
864/// ```
865///
866/// # Supported units
867///
868/// | Constructor    | Accessor     | Unit                        |
869/// |----------------|--------------|-----------------------------|
870/// | `from_mps2`   | `in_mps2`    | m/s^2                       |
871/// | `from_g`      | `in_g`       | standard gravity (g0)       |
872/// | `from_mgal`   | `in_mgal`    | milligal (mGal) -- 1e-5 m/s^2 |
873/// | `from_ftps2`  | `in_ftps2`   | ft/s^2                      |
874///
875/// # Typed arithmetic
876///
877/// - [`Acceleration`] × [`Time`] → [`Velocity`]
878/// - [`Acceleration`] × [`Mass`] → [`Force`]
879/// - [`Force`] / [`Acceleration`] → [`Mass`]
880/// - [`GravitationalParameter`](crate::GravitationalParameter) / [`Area`](crate::Area) → [`Acceleration`]
881#[must_use]
882#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
883pub struct Acceleration(pub(crate) f64);
884
885/// Display / conversion units for [`Acceleration`].
886#[derive(Debug, Clone, Copy, PartialEq, Eq)]
887pub enum AccelerationUnit {
888    /// Meter per second squared (m/s^2) -- SI derived.
889    MeterPerSecondSquared,
890    /// Standard gravity (g0) -- 9.806 65 m/s^2.
891    StandardGravity,
892    /// Milligal (mGal) -- 1e-5 m/s^2.
893    Milligal,
894    /// Foot per second squared (ft/s^2).
895    FootPerSecondSquared,
896}
897
898impl AccelerationUnit {
899    const fn symbol(self) -> &'static str {
900        match self {
901            Self::MeterPerSecondSquared => "m/s^2",
902            Self::StandardGravity => "g0",
903            Self::Milligal => "mGal",
904            Self::FootPerSecondSquared => "ft/s^2",
905        }
906    }
907
908    const fn mps2_per_unit(self) -> f64 {
909        match self {
910            Self::MeterPerSecondSquared => 1.0,
911            Self::StandardGravity => G0_MPS2,
912            Self::Milligal => 1e-5,
913            Self::FootPerSecondSquared => METERS_PER_FOOT,
914        }
915    }
916}
917
918impl Acceleration {
919    /// Create from meters per second squared (canonical unit).
920    pub const fn from_mps2(val: f64) -> Self {
921        Self(val)
922    }
923
924    /// Create from standard gravities. 1 g0 = 9.806 65 m/s^2.
925    pub const fn from_g(val: f64) -> Self {
926        Self(val * G0_MPS2)
927    }
928
929    /// Create from milligals. 1 mGal = 1e-5 m/s^2.
930    pub const fn from_mgal(val: f64) -> Self {
931        Self(val * 1e-5)
932    }
933
934    /// Create from feet per second squared. 1 ft/s^2 = 0.3048 m/s^2.
935    pub const fn from_ftps2(val: f64) -> Self {
936        Self(val * METERS_PER_FOOT)
937    }
938
939    /// Get value in meters per second squared.
940    pub const fn in_mps2(self) -> f64 {
941        self.0
942    }
943
944    /// Get value in standard gravities.
945    pub const fn in_g(self) -> f64 {
946        self.0 / G0_MPS2
947    }
948
949    /// Get value in milligals.
950    pub const fn in_mgal(self) -> f64 {
951        self.0 / 1e-5
952    }
953
954    /// Get value in feet per second squared.
955    pub const fn in_ftps2(self) -> f64 {
956        self.0 / METERS_PER_FOOT
957    }
958
959    /// Get value in the specified [`AccelerationUnit`].
960    pub fn in_unit(self, unit: AccelerationUnit) -> f64 {
961        self.0 / unit.mps2_per_unit()
962    }
963
964    /// Return a [`DisplayWithUnit`] for formatted printing in the given unit.
965    pub fn display_as(self, unit: AccelerationUnit) -> DisplayWithUnit {
966        DisplayWithUnit {
967            value: self.in_unit(unit),
968            symbol: unit.symbol(),
969        }
970    }
971
972    /// Absolute value.
973    pub fn abs(self) -> Self {
974        Self(self.0.abs())
975    }
976}
977
978impl_quantity_display!(Acceleration, "m/s^2");
979
980impl_common_ops!(Acceleration);
981
982// -------------------------
983// Force
984// -------------------------
985
986/// A force quantity, stored canonically in **Newtons** (N).
987///
988/// # Construction
989/// ```
990/// use space_units::prelude::*;
991///
992/// let f = Force::from_kn(50.0);
993/// let f2 = 50.0.kn();            // NumericExt shorthand
994/// ```
995///
996/// # Supported units
997///
998/// | Constructor  | Accessor  | Unit               |
999/// |--------------|-----------|--------------------|
1000/// | `from_n`     | `in_n`    | Newton (N)         |
1001/// | `from_kn`    | `in_kn`   | kilonewton (kN)    |
1002/// | `from_mn`    | `in_mn`   | meganewton (MN)    |
1003/// | `from_lbf`   | `in_lbf`  | pound-force (lbf)  |
1004/// | `from_dyn`   | `in_dyn`  | dyne (dyn)         |
1005///
1006/// # Typed arithmetic
1007///
1008/// - [`Force`] × [`Length`] → [`Energy`](crate::Energy)
1009/// - [`Force`] × [`Time`] → [`Impulse`]
1010/// - [`Force`] / [`Mass`] → [`Acceleration`]
1011/// - [`Force`] / [`Acceleration`] → [`Mass`]
1012/// - [`Force`] / [`Area`](crate::Area) → [`Pressure`](crate::Pressure)
1013/// - [`Force`] / [`MassFlowRate`](crate::MassFlowRate) → [`Velocity`]
1014/// - [`Force`] / [`Velocity`] → [`MassFlowRate`](crate::MassFlowRate)
1015/// - [`Energy`](crate::Energy) / [`Length`] → [`Force`]
1016#[must_use]
1017#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
1018pub struct Force(pub(crate) f64);
1019
1020/// Display / conversion units for [`Force`].
1021#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1022pub enum ForceUnit {
1023    /// Newton (N) -- SI derived unit.
1024    Newton,
1025    /// Kilonewton (kN) -- 1 000 N.
1026    KiloNewton,
1027    /// Meganewton (MN) -- 1 000 000 N.
1028    MegaNewton,
1029    /// Pound-force (lbf) -- 4.448 222 N.
1030    PoundForce,
1031    /// Dyne (dyn) -- 1e-5 N.
1032    Dyne,
1033}
1034
1035impl ForceUnit {
1036    const fn symbol(self) -> &'static str {
1037        match self {
1038            Self::Newton => "N",
1039            Self::KiloNewton => "kN",
1040            Self::MegaNewton => "MN",
1041            Self::PoundForce => "lbf",
1042            Self::Dyne => "dyn",
1043        }
1044    }
1045
1046    const fn newtons_per_unit(self) -> f64 {
1047        match self {
1048            Self::Newton => 1.0,
1049            Self::KiloNewton => 1_000.0,
1050            Self::MegaNewton => 1_000_000.0,
1051            Self::PoundForce => NEWTONS_PER_LBF,
1052            Self::Dyne => 1e-5,
1053        }
1054    }
1055}
1056
1057impl Force {
1058    /// Create from Newtons (canonical unit).
1059    pub const fn from_n(val: f64) -> Self {
1060        Self(val)
1061    }
1062
1063    /// Create from kilonewtons. 1 kN = 1 000 N.
1064    pub const fn from_kn(val: f64) -> Self {
1065        Self(val * 1_000.0)
1066    }
1067
1068    /// Create from meganewtons. 1 MN = 1 000 000 N.
1069    pub const fn from_mn(val: f64) -> Self {
1070        Self(val * 1_000_000.0)
1071    }
1072
1073    /// Create from pound-force. 1 lbf = 4.448 222 N.
1074    pub const fn from_lbf(val: f64) -> Self {
1075        Self(val * NEWTONS_PER_LBF)
1076    }
1077
1078    /// Create from dynes. 1 dyn = 1e-5 N.
1079    pub const fn from_dyn(val: f64) -> Self {
1080        Self(val * 1e-5)
1081    }
1082
1083    /// Get value in Newtons.
1084    pub const fn in_n(self) -> f64 {
1085        self.0
1086    }
1087
1088    /// Get value in kilonewtons.
1089    pub const fn in_kn(self) -> f64 {
1090        self.0 / 1_000.0
1091    }
1092
1093    /// Get value in meganewtons.
1094    pub const fn in_mn(self) -> f64 {
1095        self.0 / 1_000_000.0
1096    }
1097
1098    /// Get value in pound-force.
1099    pub const fn in_lbf(self) -> f64 {
1100        self.0 / NEWTONS_PER_LBF
1101    }
1102
1103    /// Get value in dynes.
1104    pub const fn in_dyn(self) -> f64 {
1105        self.0 / 1e-5
1106    }
1107
1108    /// Get value in the specified [`ForceUnit`].
1109    pub fn in_unit(self, unit: ForceUnit) -> f64 {
1110        self.0 / unit.newtons_per_unit()
1111    }
1112
1113    /// Return a [`DisplayWithUnit`] for formatted printing in the given unit.
1114    pub fn display_as(self, unit: ForceUnit) -> DisplayWithUnit {
1115        DisplayWithUnit {
1116            value: self.in_unit(unit),
1117            symbol: unit.symbol(),
1118        }
1119    }
1120
1121    /// Absolute value.
1122    pub fn abs(self) -> Self {
1123        Self(self.0.abs())
1124    }
1125}
1126
1127impl_quantity_display!(Force, "N");
1128
1129impl_common_ops!(Force);
1130
1131// -------------------------
1132// Impulse
1133// -------------------------
1134
1135/// An impulse quantity, stored canonically in **Newton-seconds** (N*s).
1136///
1137/// # Construction
1138/// ```
1139/// use space_units::prelude::*;
1140///
1141/// let j = Impulse::from_n_s(5000.0);
1142/// let j2 = Impulse::from_kn_s(5.0);
1143/// ```
1144///
1145/// # Supported units
1146///
1147/// | Constructor   | Accessor   | Unit                  |
1148/// |---------------|------------|-----------------------|
1149/// | `from_n_s`    | `in_n_s`   | Newton-second (N*s)   |
1150/// | `from_kn_s`   | `in_kn_s`  | kN*s                  |
1151/// | `from_lbf_s`  | `in_lbf_s` | pound-force-second    |
1152///
1153/// # Typed arithmetic
1154///
1155/// - [`Force`] × [`Time`] → [`Impulse`]
1156/// - [`Impulse`] / [`Mass`] → [`Velocity`]
1157#[must_use]
1158#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
1159pub struct Impulse(pub(crate) f64);
1160
1161/// Display / conversion units for [`Impulse`].
1162#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1163pub enum ImpulseUnit {
1164    /// Newton-second (N*s) -- SI derived.
1165    NewtonSecond,
1166    /// Kilonewton-second (kN*s).
1167    KiloNewtonSecond,
1168    /// Pound-force-second (lbf*s).
1169    PoundForceSecond,
1170}
1171
1172impl ImpulseUnit {
1173    const fn symbol(self) -> &'static str {
1174        match self {
1175            Self::NewtonSecond => "N*s",
1176            Self::KiloNewtonSecond => "kN*s",
1177            Self::PoundForceSecond => "lbf*s",
1178        }
1179    }
1180
1181    const fn newton_seconds_per_unit(self) -> f64 {
1182        match self {
1183            Self::NewtonSecond => 1.0,
1184            Self::KiloNewtonSecond => 1_000.0,
1185            Self::PoundForceSecond => NEWTONS_PER_LBF,
1186        }
1187    }
1188}
1189
1190impl Impulse {
1191    /// Create from Newton-seconds (canonical unit).
1192    pub const fn from_n_s(val: f64) -> Self {
1193        Self(val)
1194    }
1195
1196    /// Create from kilonewton-seconds. 1 kN*s = 1 000 N*s.
1197    pub const fn from_kn_s(val: f64) -> Self {
1198        Self(val * 1_000.0)
1199    }
1200
1201    /// Create from pound-force-seconds. 1 lbf*s = 4.448 222 N*s.
1202    pub const fn from_lbf_s(val: f64) -> Self {
1203        Self(val * NEWTONS_PER_LBF)
1204    }
1205
1206    /// Get value in Newton-seconds.
1207    pub const fn in_n_s(self) -> f64 {
1208        self.0
1209    }
1210
1211    /// Get value in kilonewton-seconds.
1212    pub const fn in_kn_s(self) -> f64 {
1213        self.0 / 1_000.0
1214    }
1215
1216    /// Get value in pound-force-seconds.
1217    pub const fn in_lbf_s(self) -> f64 {
1218        self.0 / NEWTONS_PER_LBF
1219    }
1220
1221    /// Get value in the specified [`ImpulseUnit`].
1222    pub fn in_unit(self, unit: ImpulseUnit) -> f64 {
1223        self.0 / unit.newton_seconds_per_unit()
1224    }
1225
1226    /// Return a [`DisplayWithUnit`] for formatted printing in the given unit.
1227    pub fn display_as(self, unit: ImpulseUnit) -> DisplayWithUnit {
1228        DisplayWithUnit {
1229            value: self.in_unit(unit),
1230            symbol: unit.symbol(),
1231        }
1232    }
1233
1234    /// Absolute value.
1235    pub fn abs(self) -> Self {
1236        Self(self.0.abs())
1237    }
1238}
1239
1240impl_quantity_display!(Impulse, "N\u{00b7}s");
1241
1242impl_common_ops!(Impulse);
1243
1244// -------------------------
1245// Momentum
1246// -------------------------
1247
1248/// A momentum quantity, stored canonically in **kg*m/s**.
1249///
1250/// # Construction
1251/// ```
1252/// use space_units::prelude::*;
1253///
1254/// let p = Momentum::from_kg_mps(1000.0);
1255/// ```
1256///
1257/// # Supported units
1258///
1259/// | Constructor      | Accessor      | Unit          |
1260/// |------------------|---------------|---------------|
1261/// | `from_kg_mps`    | `in_kg_mps`   | kg*m/s        |
1262///
1263/// # Typed arithmetic
1264///
1265/// - [`Mass`] × [`Velocity`] → [`Momentum`]
1266/// - [`Momentum`] / [`Mass`] → [`Velocity`]
1267/// - [`Momentum`] / [`Velocity`] → [`Mass`]
1268#[must_use]
1269#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
1270pub struct Momentum(pub(crate) f64);
1271
1272impl Momentum {
1273    /// Create from kg*m/s (canonical and only unit).
1274    pub const fn from_kg_mps(val: f64) -> Self {
1275        Self(val)
1276    }
1277
1278    /// Get value in kg*m/s.
1279    pub const fn in_kg_mps(self) -> f64 {
1280        self.0
1281    }
1282
1283    /// Absolute value.
1284    pub fn abs(self) -> Self {
1285        Self(self.0.abs())
1286    }
1287}
1288
1289impl_quantity_display!(Momentum, "kg\u{00b7}m/s");
1290
1291impl_common_ops!(Momentum);