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);