RIT VEXU Core API
Loading...
Searching...
No Matches
units.h
1#pragma once
2
3#include <algorithm>
4#include <cevalm.hpp>
5#include <cmath>
6#include <concepts>
7#include <numbers>
8#include <ratio>
9#include <type_traits>
10#include <utility>
11
12namespace units {
13
21template <typename Mass = std::ratio<0>, typename Length = std::ratio<0>,
22 typename Time = std::ratio<0>, typename Current = std::ratio<0>,
23 typename Angle = std::ratio<0>, typename Temperature = std::ratio<0>,
24 typename Luminosity = std::ratio<0>, typename Moles = std::ratio<0>>
25class Quantity {
26protected:
27 double value;
28
29public:
30 using mass = Mass;
31 using length = Length;
32 using time = Time;
33 using current = Current;
34 using angle = Angle;
35 using temperature = Temperature;
36 using luminosity = Luminosity;
37 using moles = Moles;
38
39 using Self = Quantity<Mass, Length, Time, Current, Angle, Temperature,
40 Luminosity, Moles>;
41 static constexpr bool is_dimensionless = std::is_same_v<Self, Quantity<>>;
42
46 explicit constexpr Quantity() : value(0) {}
47
53 explicit constexpr Quantity(double value)
54 requires(!is_dimensionless)
55 : value(value) {}
56
62 constexpr Quantity(double value)
63 requires is_dimensionless
64 : value(value) {}
65
72 constexpr Quantity(double value, Self unit) : value(value * unit.value) {}
73
79 constexpr operator double() const
80 requires is_dimensionless
81 {
82 return value;
83 }
84
90 constexpr Quantity(const Self &other) = default;
91
97 constexpr double internal() const { return value; }
98
103 constexpr double to(Self unit) const { return value / unit.value; }
104
108 constexpr Self &operator+=(Self other) {
109 value += other.value;
110 return *this;
111 }
112
117 constexpr Self &operator+=(double other)
118 requires is_dimensionless
119 {
120 value += other;
121 return *this;
122 }
123
127 constexpr Self &operator-=(Self other) {
128 value -= other.value;
129 return *this;
130 }
131
136 constexpr Self &operator-=(double other)
137 requires is_dimensionless
138 {
139 value -= other;
140 return *this;
141 }
142
146 constexpr Self &operator*=(double scalar) {
147 value *= scalar;
148 return *this;
149 }
150
154 constexpr Self &operator/=(double divisor) {
155 value /= divisor;
156 return *this;
157 }
158
163 constexpr Self &operator=(const double &rhs)
164 requires is_dimensionless
165 {
166 value = rhs;
167 return *this;
168 }
169};
170
174using Number = Quantity<>;
175
179template <typename Mass = std::ratio<0>, typename Length = std::ratio<0>,
180 typename Time = std::ratio<0>, typename Current = std::ratio<0>,
181 typename Angle = std::ratio<0>, typename Temperature = std::ratio<0>,
182 typename Luminosity = std::ratio<0>, typename Moles = std::ratio<0>>
183void quantity_checker(Quantity<Mass, Length, Time, Current, Angle, Temperature,
184 Luminosity, Moles>) {}
185
189template <typename Q>
190concept IsQuantity = requires(Q q) { quantity_checker(q); };
191
196template <IsQuantity Q> constexpr Q from(double value, Q unit) {
197 return Q(value * unit.internal());
198}
199
203template <typename Q, typename... Quantities>
204concept Isomorphic = ((std::convertible_to<Q, Quantities> &&
205 std::convertible_to<Quantities, Q>) &&
206 ...);
207
211template <IsQuantity Q1, IsQuantity Q2>
212using Multiplied =
214 std::ratio_add<typename Q1::length, typename Q2::length>,
215 std::ratio_add<typename Q1::time, typename Q2::time>,
216 std::ratio_add<typename Q1::current, typename Q2::current>,
217 std::ratio_add<typename Q1::angle, typename Q2::angle>,
218 std::ratio_add<typename Q1::temperature, typename Q2::temperature>,
219 std::ratio_add<typename Q1::luminosity, typename Q2::luminosity>,
220 std::ratio_add<typename Q1::moles, typename Q2::moles>>;
221
225template <IsQuantity Q1, IsQuantity Q2>
226using Divided = Quantity<
227 std::ratio_subtract<typename Q1::mass, typename Q2::mass>,
228 std::ratio_subtract<typename Q1::length, typename Q2::length>,
229 std::ratio_subtract<typename Q1::time, typename Q2::time>,
230 std::ratio_subtract<typename Q1::current, typename Q2::current>,
231 std::ratio_subtract<typename Q1::angle, typename Q2::angle>,
232 std::ratio_subtract<typename Q1::temperature, typename Q2::temperature>,
233 std::ratio_subtract<typename Q1::luminosity, typename Q2::luminosity>,
234 std::ratio_subtract<typename Q1::moles, typename Q2::moles>>;
235
239template <IsQuantity Q, typename factor>
240using Exponentiated =
242 std::ratio_multiply<typename Q::length, factor>,
243 std::ratio_multiply<typename Q::time, factor>,
244 std::ratio_multiply<typename Q::current, factor>,
245 std::ratio_multiply<typename Q::angle, factor>,
246 std::ratio_multiply<typename Q::temperature, factor>,
247 std::ratio_multiply<typename Q::luminosity, factor>,
248 std::ratio_multiply<typename Q::moles, factor>>;
249
253template <IsQuantity Q, typename quotient>
255 std::ratio_divide<typename Q::length, quotient>,
256 std::ratio_divide<typename Q::time, quotient>,
257 std::ratio_divide<typename Q::current, quotient>,
258 std::ratio_divide<typename Q::angle, quotient>,
259 std::ratio_divide<typename Q::temperature, quotient>,
260 std::ratio_divide<typename Q::luminosity, quotient>,
261 std::ratio_divide<typename Q::moles, quotient>>;
262
263template <IsQuantity Q> constexpr Q operator+(Q rhs) { return rhs; }
264
268template <IsQuantity Q, IsQuantity R>
269constexpr Q operator+(Q lhs, R rhs)
270 requires Isomorphic<Q, R>
271{
272 return Q(lhs.internal() + rhs.internal());
273}
274
278template <IsQuantity Q> constexpr Q operator-(Q rhs) {
279 return Q(-rhs.internal());
280}
281
285template <IsQuantity Q, IsQuantity R>
286constexpr Q operator-(Q lhs, R rhs)
287 requires Isomorphic<Q, R>
288{
289 return Q(lhs.internal() - rhs.internal());
290}
291
295template <IsQuantity Q>
296 requires(!std::is_same_v<Q, Number>)
297constexpr Q operator*(Q quantity, Number multiple) {
298 return Q(quantity.internal() * multiple.internal());
299}
300
304template <IsQuantity Q>
305 requires(!std::is_same_v<Q, Number>)
306constexpr Q operator*(Number multiple, Q quantity) {
307 return Q(quantity.internal() * multiple.internal());
308}
309
313template <IsQuantity Q>
314 requires(!std::is_same_v<Q, Number>)
315constexpr Q operator/(Q quantity, Number divisor) {
316 return Q(quantity.internal() / divisor.internal());
317}
318
322template <IsQuantity Q>
323 requires(!std::is_same_v<Q, Number>)
324constexpr auto operator/(Number enumerator, Q divisor) {
325 return Divided<Number, Q>(enumerator.internal() / divisor.internal());
326}
327
331template <IsQuantity Q1, IsQuantity Q2>
332constexpr auto operator*(Q1 lhs, Q2 rhs) {
333 return Multiplied<Q1, Q2>(lhs.internal() * rhs.internal());
334}
335
339template <IsQuantity Q1, IsQuantity Q2>
340constexpr auto operator/(Q1 lhs, Q2 rhs) {
341 return Divided<Q1, Q2>(lhs.internal() / rhs.internal());
342}
343
347template <IsQuantity Q, IsQuantity R>
348constexpr bool operator==(const Q &lhs, const R &rhs)
349 requires Isomorphic<Q, R>
350{
351 return (lhs.internal() == rhs.internal());
352}
353
357template <IsQuantity Q, IsQuantity R>
358constexpr bool operator!=(const Q &lhs, const R &rhs)
359 requires Isomorphic<Q, R>
360{
361 return (lhs.internal() != rhs.internal());
362}
363
367template <IsQuantity Q, IsQuantity R>
368constexpr bool operator<=(const Q &lhs, const R &rhs)
369 requires Isomorphic<Q, R>
370{
371 return (lhs.internal() <= rhs.internal());
372}
373
377template <IsQuantity Q, IsQuantity R>
378constexpr bool operator>=(const Q &lhs, const R &rhs)
379 requires Isomorphic<Q, R>
380{
381 return (lhs.internal() >= rhs.internal());
382}
383
387template <IsQuantity Q, IsQuantity R>
388constexpr bool operator<(const Q &lhs, const R &rhs)
389 requires Isomorphic<Q, R>
390{
391 return (lhs.internal() < rhs.internal());
392}
393
397template <IsQuantity Q, IsQuantity R>
398constexpr bool operator>(const Q &lhs, const R &rhs)
399 requires Isomorphic<Q, R>
400{
401 return (lhs.internal() > rhs.internal());
402}
403
419#define NEW_UNIT(Name, long_suffix, suffix, m, l, t, i, a, o, j, n) \
420 using Name = \
421 Quantity<std::ratio<m>, std::ratio<l>, std::ratio<t>, std::ratio<i>, \
422 std::ratio<a>, std::ratio<o>, std::ratio<j>, std::ratio<n>>; \
423 [[maybe_unused]] \
424 constexpr Name long_suffix = Name(1.0); \
425 constexpr Name suffix = long_suffix; \
426 namespace literals { \
427 constexpr Name operator""_##long_suffix(long double value) { \
428 return Name( \
429 Quantity<std::ratio<m>, std::ratio<l>, std::ratio<t>, std::ratio<i>, \
430 std::ratio<a>, std::ratio<o>, std::ratio<j>, std::ratio<n>>( \
431 static_cast<double>(value))); \
432 } \
433 constexpr Name operator""_##long_suffix(unsigned long long value) { \
434 return Name( \
435 Quantity<std::ratio<m>, std::ratio<l>, std::ratio<t>, std::ratio<i>, \
436 std::ratio<a>, std::ratio<o>, std::ratio<j>, std::ratio<n>>( \
437 static_cast<double>(value))); \
438 } \
439 constexpr Name operator""_##suffix(long double value) { \
440 return Name( \
441 Quantity<std::ratio<m>, std::ratio<l>, std::ratio<t>, std::ratio<i>, \
442 std::ratio<a>, std::ratio<o>, std::ratio<j>, std::ratio<n>>( \
443 static_cast<double>(value))); \
444 } \
445 constexpr Name operator""_##suffix(unsigned long long value) { \
446 return Name( \
447 Quantity<std::ratio<m>, std::ratio<l>, std::ratio<t>, std::ratio<i>, \
448 std::ratio<a>, std::ratio<o>, std::ratio<j>, std::ratio<n>>( \
449 static_cast<double>(value))); \
450 } \
451 }
452
453#define NEW_UNIT_LITERAL(Name, long_suffix, suffix, multiple) \
454 [[maybe_unused]] \
455 constexpr Name suffix = multiple; \
456 constexpr Name long_suffix = multiple; \
457 namespace literals { \
458 constexpr Name operator""_##long_suffix(long double value) { \
459 return static_cast<double>(value) * multiple; \
460 } \
461 constexpr Name operator""_##suffix(long double value) { \
462 return static_cast<double>(value) * multiple; \
463 } \
464 constexpr Name operator""_##long_suffix(unsigned long long value) { \
465 return static_cast<double>(value) * multiple; \
466 } \
467 constexpr Name operator""_##suffix(unsigned long long value) { \
468 return static_cast<double>(value) * multiple; \
469 } \
470 }
471
472#define NEW_METRIC_PREFIXES(Name, long_base, base) \
473 NEW_UNIT_LITERAL(Name, tera##long_base, T##base, base * 1E12) \
474 NEW_UNIT_LITERAL(Name, giga##long_base, G##base, base * 1E9) \
475 NEW_UNIT_LITERAL(Name, mega##long_base, M##base, base * 1E6) \
476 NEW_UNIT_LITERAL(Name, kilo##long_base, k##base, base * 1E3) \
477 NEW_UNIT_LITERAL(Name, centi##long_base, c##base, base / 1E2) \
478 NEW_UNIT_LITERAL(Name, milli##long_base, m##base, base / 1E3) \
479 NEW_UNIT_LITERAL(Name, micro##long_base, u##base, base / 1E6) \
480 NEW_UNIT_LITERAL(Name, nano##long_base, n##base, base / 1E9) \
481 NEW_UNIT_LITERAL(Name, pico##long_base, p##base, base / 1E12)
482
483NEW_UNIT(Mass, kilograms, kg, 1, 0, 0, 0, 0, 0, 0, 0)
484NEW_UNIT_LITERAL(Mass, grams, g, kg / 1000)
485NEW_UNIT_LITERAL(Mass, ounces, oz, g * 28.349523125)
486NEW_UNIT_LITERAL(Mass, pounds_mass, lbm, oz * 16)
487NEW_UNIT_LITERAL(Mass, tons, ton, lbm * 2000)
488
489NEW_UNIT(Time, seconds, s, 0, 0, 1, 0, 0, 0, 0, 0)
490NEW_METRIC_PREFIXES(Time, seconds, s)
491NEW_UNIT_LITERAL(Time, minutes, mins, s * 60)
492NEW_UNIT_LITERAL(Time, hours, hr, mins * 60)
493NEW_UNIT_LITERAL(Time, days, day, hr * 24)
494
495NEW_UNIT(Length, meters, m, 0, 1, 0, 0, 0, 0, 0, 0)
496NEW_METRIC_PREFIXES(Length, meters, m)
497NEW_UNIT_LITERAL(Length, inches, in, cm * 2.54)
498NEW_UNIT_LITERAL(Length, feet, ft, in * 12)
499NEW_UNIT_LITERAL(Length, yards, yd, ft * 3)
500NEW_UNIT_LITERAL(Length, miles, mi, ft * 5280)
502NEW_UNIT_LITERAL(Length, tiles, tile, in * 23.75)
503
504NEW_UNIT(Area, square_meters, m2, 0, 2, 0, 0, 0, 0, 0, 0)
505NEW_UNIT_LITERAL(Area, square_yards, yd2, yd * yd)
506NEW_UNIT_LITERAL(Area, square_feet, ft2, ft * ft)
507NEW_UNIT_LITERAL(Area, square_inches, in2, in * in)
508NEW_UNIT_LITERAL(Area, acres, acre, yd2 * 4840)
509
510NEW_UNIT(Volume, cubic_meters, m3, 0, 3, 0, 0, 0, 0, 0, 0)
511NEW_UNIT_LITERAL(Volume, cubic_feet, ft3, ft * ft * ft)
512NEW_UNIT_LITERAL(Volume, cubic_inches, in3, in * in * in)
513NEW_UNIT_LITERAL(Volume, liters, liter, m3 * 0.001)
514NEW_UNIT_LITERAL(Volume, gallons, gal, in3 * 231)
515
516NEW_UNIT(Velocity, meters_per_second, mps, 0, 1, -1, 0, 0, 0, 0, 0)
517NEW_METRIC_PREFIXES(Velocity, mps, mps)
518NEW_UNIT_LITERAL(Velocity, inches_per_second, inps, in / s)
519NEW_UNIT_LITERAL(Velocity, feet_per_second, ftps, ft / s)
520NEW_UNIT_LITERAL(Velocity, miles_per_hour, miph, mi / hr)
521
522NEW_UNIT(Acceleration, meters_per_second_squared, mps2, 0, 1, -2, 0, 0, 0, 0, 0)
523NEW_METRIC_PREFIXES(Acceleration, mps2, mps2)
524NEW_UNIT_LITERAL(Acceleration, inches_per_second_squared, inps2, in / (s * s))
525
526NEW_UNIT(Jerk, meters_per_second_cubed, mps3, 0, 1, -3, 0, 0, 0, 0, 0)
527NEW_METRIC_PREFIXES(Jerk, mps3, mps3)
528NEW_UNIT_LITERAL(Jerk, inches_per_second_cubed, inps3, in / (s * s * s))
529
530NEW_UNIT(Absement, meter_seconds, m_s, 0, 1, 1, 0, 0, 0, 0, 0)
531NEW_UNIT_LITERAL(Absement, inch_seconds, in_s, in * s)
532
533NEW_UNIT(Angle, radians, rad, 0, 0, 0, 0, 1, 0, 0, 0)
534NEW_UNIT_LITERAL(Angle, degrees, deg, rad * std::numbers::pi / 180)
535NEW_UNIT_LITERAL(Angle, revolutions, rev, rad * std::numbers::pi * 2)
536NEW_UNIT_LITERAL(Angle, gradians, grad, deg * 0.9)
537
538NEW_UNIT(AngularVelocity, radians_per_second, radps, 0, 0, -1, 0, 1, 0, 0, 0)
539NEW_UNIT_LITERAL(AngularVelocity, degrees_per_second, dps, deg / s)
540NEW_UNIT_LITERAL(AngularVelocity, revolutions_per_second, revps, rev / s)
541NEW_UNIT_LITERAL(AngularVelocity, revolutions_per_minute, rpm, rev / mins)
542
543NEW_UNIT(AngularAcceleration, radians_per_second_squared, radps2, 0, 0, -2, 0,
544 1, 0, 0, 0)
545NEW_UNIT_LITERAL(AngularAcceleration, degrees_per_second_squared, dps2,
546 deg / (s * s))
547NEW_UNIT_LITERAL(AngularAcceleration, revolutions_per_second_squared, revps2,
548 rev / (s * s))
549
550NEW_UNIT(AngularJerk, radians_per_second_cubed, radps3, 0, 0, -3, 0, 1, 0, 0, 0)
551NEW_UNIT_LITERAL(AngularJerk, degrees_per_second_cubed, dps3, deg / (s * s * s))
552NEW_UNIT_LITERAL(AngularJerk, revolutions_per_second_cubed, revps3,
553 rev / (s * s * s))
554
555NEW_UNIT(AngularAbsement, radian_seconds, rad_s, 0, 0, 1, 0, 1, 0, 0, 0)
556NEW_UNIT_LITERAL(AngularAbsement, degree_seconds, d_s, deg * s)
557NEW_UNIT_LITERAL(AngularAbsement, revolution_seconds, rev_s, rev * s)
558
559NEW_UNIT(Curvature, radians_per_meter, radpm, 0, -1, 0, 0, 1, 0, 0, 0)
560NEW_UNIT_LITERAL(Curvature, degrees_per_meter, degpm, deg / m)
561NEW_UNIT_LITERAL(Curvature, radians_per_inch, radpin, rad / in)
562NEW_UNIT_LITERAL(Curvature, degrees_per_inch, degpin, deg / in)
563
564NEW_UNIT(Frequency, hertz, Hz, 0, 0, -1, 0, 0, 0, 0, 0)
565NEW_METRIC_PREFIXES(Frequency, hertz, Hz)
566
567NEW_UNIT(Charge, coulombs, coulomb, 0, 0, 1, 1, 0, 0, 0, 0)
568
569NEW_UNIT(Voltage, volts, V, 1, 2, -3, -1, 0, 0, 0, 0)
570NEW_METRIC_PREFIXES(Voltage, volts, V)
571
572NEW_UNIT(Resistance, ohms, ohm, 1, 2, -3, -2, 0, 0, 0, 0)
573NEW_METRIC_PREFIXES(Resistance, ohms, ohm)
574
575NEW_UNIT(Conductance, siemens, S, -1, -2, 3, 2, 0, 0, 0, 0)
576NEW_METRIC_PREFIXES(Conductance, siemens, S)
577
578NEW_UNIT(Capacitance, farads, F, -1, -2, 4, 2, 0, 0, 0, 0)
579NEW_METRIC_PREFIXES(Capacitance, farads, F)
580
581NEW_UNIT(Inductance, henries, H, 1, 2, -2, -2, 0, 0, 0, 0)
582NEW_METRIC_PREFIXES(Inductance, henries, H)
583
584NEW_UNIT(Force, newtons, N, 1, 1, -2, 0, 0, 0, 0, 0)
585NEW_METRIC_PREFIXES(Force, newtons, N)
586NEW_UNIT_LITERAL(Force, pounds_force, lbf, lbm * 9.80665 * m / (s * s))
587
588NEW_UNIT(Power, watts, W, 1, 2, -3, 0, 0, 0, 0, 0)
589NEW_METRIC_PREFIXES(Power, watts, W)
590
591NEW_UNIT(Momentum, kilogram_meters_per_second, kgmps, 1, 1, -1, 0, 0, 0, 0, 0)
592NEW_UNIT_LITERAL(Momentum, newton_seconds, Ns, N * s)
593
594/*
595 * Inertia as in moment of inertia is divided by radians^2
596 * normally kg*m^2, but also J*s^2 / rad^2... rad is normally dimensionless, not
597 * here
598 */
599NEW_UNIT(Inertia, kilogram_meters_squared, kgm2, 1, 2, 0, 0, -2, 0, 0, 0)
600
601NEW_UNIT(Energy, joules, J, 1, 2, -2, 0, 0, 0, 0, 0)
602NEW_METRIC_PREFIXES(Energy, joules, J)
603
604/*
605 * Torque and Energy are NOT the same here, torque is divided by radians...
606 * as in, energy per radian. J and Nm are normally the same but here they're not
607 */
608NEW_UNIT(Torque, newton_meters, Nm, 1, 2, -2, 0, -1, 0, 0, 0)
609NEW_UNIT_LITERAL(Torque, pound_feet, lbft, Nm * 1.3558179483)
610
611NEW_UNIT(Current, amps, A, 0, 0, 0, 1, 0, 0, 0, 0)
612NEW_METRIC_PREFIXES(Current, amps, A)
613
614NEW_UNIT(LuminousIntensity, candelas, candela, 0, 0, 0, 0, 0, 0, 1, 0)
615
616NEW_UNIT(Amount, moles, mol, 0, 0, 0, 0, 0, 0, 0, 1)
617
618NEW_UNIT(LinearVelocityFeedforward, volts_per_meter_per_second, VpMps, 1, 1, -2,
619 -1, 0, 0, 0, 0)
620NEW_UNIT_LITERAL(LinearVelocityFeedforward, volts_per_inch_per_second, VpInps,
621 V / inps)
622
623NEW_UNIT(LinearProportionalGain, volts_per_meter, VpM, 1, 1, -3, -1, 0, 0, 0, 0)
624NEW_UNIT_LITERAL(LinearProportionalGain, volts_per_inch, VpIn, V / in)
625
626NEW_UNIT(LinearIntegralGain, volts_per_meter_second, VpMS, 1, 1, -4, -1, 0, 0,
627 0, 0)
628NEW_UNIT_LITERAL(LinearIntegralGain, volts_per_inch_second, VpInS, V / (in * s))
629
630NEW_UNIT(LinearAccelerationFeedforward, volts_per_meter_per_second_squared,
631 VpMps2, 1, 1, -1, -1, 0, 0, 0, 0)
632NEW_UNIT_LITERAL(LinearAccelerationFeedforward,
633 volts_per_inch_per_second_squared, VpInps2, V / inps2)
634
635NEW_UNIT(LinearDerivativeGain, volt_seconds_per_meter, VspM, 1, 1, -2, -1, 0, 0,
636 0, 0)
637NEW_UNIT_LITERAL(LinearDerivativeGain, volt_seconds_per_inch, VspIn, V / inps)
638
639/*
640 * These are the same dimension, and we don't have special names, so "using" is
641 * cleanest
642 */
643using LinearVelocityProportionalGain = LinearDerivativeGain;
644
645NEW_UNIT(LinearVelocityDerivativeGain, volt_seconds_squared_per_meter, Vs2pM, 1,
646 1, -1, -1, 0, 0, 0, 0)
647NEW_UNIT_LITERAL(LinearVelocityDerivativeGain, volt_seconds_squared_per_inch,
648 Vs2pIn, V / inps2)
649
650using LinearVelocityIntegralGain = LinearProportionalGain;
651
652NEW_UNIT(AngularVelocityFeedforward, volts_per_radian_per_second, VpRadPs, 1, 2,
653 -2, -1, -1, 0, 0, 0)
654NEW_UNIT_LITERAL(AngularVelocityFeedforward, volts_per_degree_per_second,
655 VpDegPs, V / dps)
656
657NEW_UNIT(AngularProportionalGain, volts_per_radian, VpRad, 1, 2, -3, -1, -1, 0,
658 0, 0)
659NEW_UNIT_LITERAL(AngularProportionalGain, volts_per_degree, VpDeg, V / deg)
660
661NEW_UNIT(AngularIntegralGain, volts_per_radian_second, VpRadS, 1, 2, -4, -1, -1,
662 0, 0, 0)
663NEW_UNIT_LITERAL(AngularIntegralGain, volts_per_degree_second, VpDegS,
664 V / (deg * s))
665
666NEW_UNIT(AngularAccelerationFeedforward, volts_per_radian_per_second_squared,
667 VpRadPs2, 1, 2, -1, -1, -1, 0, 0, 0)
668NEW_UNIT_LITERAL(AngularAccelerationFeedforward,
669 volts_per_degree_per_second_squared, VpDegPs2, V / dps2)
670
671NEW_UNIT(AngularDerivativeGain, volt_seconds_per_radian, VspRad, 1, 2, -2, -1,
672 -1, 0, 0, 0)
673NEW_UNIT_LITERAL(AngularDerivativeGain, volt_seconds_per_degree, VspDeg,
674 V / dps)
675
676using AngularVelocityProportionalGain = AngularDerivativeGain;
677
678NEW_UNIT(AngularVelocityDerivativeGain, volt_seconds_squared_per_radian,
679 Vs2pRad, 1, 2, -1, -1, -1, 0, 0, 0)
680NEW_UNIT_LITERAL(AngularVelocityDerivativeGain, volt_seconds_squared_per_degree,
681 Vs2pDeg, V / dps2)
682
683using AngularVelocityIntegralGain = AngularProportionalGain;
684
685#undef NEW_METRIC_PREFIXES
686#undef NEW_UNIT_LITERAL
687#undef NEW_UNIT
688
689/*
690 * Temperature gets special treatment since its conversions are affine.
691 * Addition subtraction and negation are all just not allowed. Do it manually.
692 */
693using TemperatureQuantity =
694 Quantity<std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>,
695 std::ratio<0>, std::ratio<1>, std::ratio<0>, std::ratio<0>>;
696
697enum class TemperatureUnit { Kelvin, Celsius, Fahrenheit };
698
699class Temperature : public TemperatureQuantity {
700public:
701 explicit constexpr Temperature() = default;
702 explicit constexpr Temperature(double kelvins)
703 : TemperatureQuantity(kelvins) {}
704
705 constexpr double to(TemperatureUnit unit) const;
706
707 constexpr Temperature &operator+=(Temperature) = delete;
708 constexpr Temperature &operator-=(Temperature) = delete;
709};
710
711constexpr TemperatureUnit kelvin = TemperatureUnit::Kelvin;
712constexpr TemperatureUnit K = kelvin;
713constexpr TemperatureUnit celsius = TemperatureUnit::Celsius;
714constexpr TemperatureUnit fahrenheit = TemperatureUnit::Fahrenheit;
715
716constexpr Temperature from(double value, TemperatureUnit unit) {
717 switch (unit) {
718 case TemperatureUnit::Kelvin:
719 return Temperature(value);
720 case TemperatureUnit::Celsius:
721 return Temperature(value + 273.15);
722 case TemperatureUnit::Fahrenheit:
723 return Temperature((value - 32.0) * (5.0 / 9.0) + 273.15);
724 }
725 return Temperature(value);
726}
727
728constexpr double Temperature::to(TemperatureUnit unit) const {
729 switch (unit) {
730 case TemperatureUnit::Kelvin:
731 return internal();
732 case TemperatureUnit::Celsius:
733 return internal() - 273.15;
734 case TemperatureUnit::Fahrenheit:
735 return (internal() - 273.15) * (9.0 / 5.0) + 32.0;
736 }
737 return internal();
738}
739
740constexpr Temperature operator+(Temperature) = delete;
741constexpr Temperature operator-(Temperature) = delete;
742constexpr Temperature operator+(Temperature, Temperature) = delete;
743constexpr Temperature operator-(Temperature, Temperature) = delete;
744
745namespace literals {
746
747constexpr Temperature operator""_kelvin(long double value) {
748 return from(static_cast<double>(value), kelvin);
749}
750
751constexpr Temperature operator""_kelvin(unsigned long long value) {
752 return from(static_cast<double>(value), kelvin);
753}
754
755constexpr Temperature operator""_K(long double value) {
756 return from(static_cast<double>(value), kelvin);
757}
758
759constexpr Temperature operator""_K(unsigned long long value) {
760 return from(static_cast<double>(value), kelvin);
761}
762
763constexpr Temperature operator""_celsius(long double value) {
764 return from(static_cast<double>(value), celsius);
765}
766
767constexpr Temperature operator""_celsius(unsigned long long value) {
768 return from(static_cast<double>(value), celsius);
769}
770
771constexpr Temperature operator""_fahrenheit(long double value) {
772 return from(static_cast<double>(value), fahrenheit);
773}
774
775constexpr Temperature operator""_fahrenheit(unsigned long long value) {
776 return from(static_cast<double>(value), fahrenheit);
777}
778
779} // namespace literals
780
785template <typename T>
786constexpr auto to_quantity(T value)
787 -> std::conditional_t<std::is_arithmetic_v<T>, Number, T> {
788 if constexpr (std::is_arithmetic_v<T>)
789 return Number(value);
790 else
791 return value;
792}
793
794template <typename T>
795using quantity_type = decltype(to_quantity(std::declval<T>()));
796
797template <typename T, typename... U>
798concept IsomorphicValues = Isomorphic<quantity_type<T>, quantity_type<U>...>;
799
800/*
801 * Using this first function as an example, the "if consteval" checks whether
802 * it is being executed at compile time or runtime.
803 * cevalm::abs is consteval, std::abs is runtime only. We prefer std::abs at
804 * runtime since it uses libm (or compiler intrinsics) for this specific cpu
805 * rather than cevalm which reimplements fdlibm manually.
806 */
807template <typename T> constexpr auto abs(const T &lhs) {
808 auto q = to_quantity(lhs);
809 using Q = decltype(q);
810 if consteval {
811 return Q(cevalm::abs(q.internal()));
812 } else {
813 return Q(std::abs(q.internal()));
814 }
815}
816
817template <typename T, typename U>
818 requires IsomorphicValues<T, U>
819constexpr auto max(const T &lhs, const U &rhs) {
820 auto qlhs = to_quantity(lhs);
821 auto qrhs = to_quantity(rhs);
822 return (qlhs > qrhs ? qlhs : qrhs);
823}
824
825template <typename T, typename U>
826 requires IsomorphicValues<T, U>
827constexpr auto min(const T &lhs, const U &rhs) {
828 auto qlhs = to_quantity(lhs);
829 auto qrhs = to_quantity(rhs);
830 return (qlhs < qrhs ? qlhs : qrhs);
831}
832
833template <typename T> constexpr auto sgn(const T &lhs) {
834 auto q = to_quantity(lhs);
835 if (q.internal() > 0)
836 return Number(1);
837 if (q.internal() < 0)
838 return Number(-1);
839 return Number(0);
840}
841
842template <int R, typename T>
844constexpr auto pow(const T &lhs) {
845 if constexpr (R == 0) {
846 return Number(1.0);
847 } else {
848 auto q = to_quantity(lhs);
849 using Q = decltype(q);
850 using S = Exponentiated<Q, std::ratio<R>>;
851 if consteval {
852 return S(cevalm::pow(q.internal(), R));
853 } else {
854 return S(std::pow(q.internal(), R));
855 }
856 }
857}
858
859template <typename T> constexpr auto square(const T &lhs) { return lhs * lhs; }
860
861template <typename T> constexpr auto cube(const T &lhs) {
862 return lhs * lhs * lhs;
863}
864
865template <int R, typename T>
866constexpr auto root(const T &lhs)
867 requires(R > 0)
868{
869 auto q = to_quantity(lhs);
870 using Q = decltype(q);
871 using S = Rooted<Q, std::ratio<R>>;
872
873 const double val = q.internal();
874 const bool odd_and_negative = (R % 2 != 0) && (val < 0);
875 const double base = odd_and_negative ? -val : val;
876
877 double res{};
878
879 if (R == 2) {
880 if consteval {
881 res = cevalm::sqrt(base);
882 } else {
883 res = std::sqrt(base);
884 }
885 } else if (R == 3) {
886 if consteval {
887 res = cevalm::cbrt(base);
888 } else {
889 res = std::cbrt(base);
890 }
891 } else {
892 if consteval {
893 res = cevalm::pow(base, 1.0 / R);
894 } else {
895 res = std::pow(base, 1.0 / R);
896 }
897 }
898
899 return S(odd_and_negative ? -res : res);
900}
901
902template <typename T> constexpr auto sqrt(const T &lhs) { return root<2>(lhs); }
903
904template <typename T> constexpr auto cbrt(const T &lhs) { return root<3>(lhs); }
905
906template <typename T, typename U>
907 requires IsomorphicValues<T, U>
908constexpr auto hypot(const T &lhs, const U &rhs) {
909 auto qlhs = to_quantity(lhs);
910 auto qrhs = to_quantity(rhs);
911 if consteval {
912 return decltype(qlhs)(cevalm::hypot(qlhs.internal(), qrhs.internal()));
913 } else {
914 return decltype(qlhs)(std::hypot(qlhs.internal(), qrhs.internal()));
915 }
916}
917
918template <typename T, typename U>
919 requires IsomorphicValues<T, U>
920constexpr auto mod(const T &lhs, const U &rhs) {
921 auto qlhs = to_quantity(lhs);
922 auto qrhs = to_quantity(rhs);
923 if consteval {
924 return decltype(qlhs)(cevalm::fmod(qlhs.internal(), qrhs.internal()));
925 } else {
926 return decltype(qlhs)(std::fmod(qlhs.internal(), qrhs.internal()));
927 }
928}
929
930template <typename T, typename U>
931constexpr auto copysign(const T &lhs, const U &rhs) {
932 auto qlhs = to_quantity(lhs);
933 auto qrhs = to_quantity(rhs);
934 if consteval {
935 return decltype(qlhs)(cevalm::copysign(qlhs.internal(), qrhs.internal()));
936 } else {
937 return decltype(qlhs)(std::copysign(qlhs.internal(), qrhs.internal()));
938 }
939}
940
941template <typename T> constexpr auto signbit(const T &lhs) {
942 auto q = to_quantity(lhs);
943 return std::signbit(q.internal());
944}
945
946template <typename T, typename U, typename V>
947 requires IsomorphicValues<T, U, V>
948constexpr auto clamp(const T &lhs, const U &lo, const V &hi) {
949 auto qlhs = to_quantity(lhs);
950 auto qlo = to_quantity(lo);
951 auto qhi = to_quantity(hi);
952 return decltype(qlhs)(
953 std::clamp(qlhs.internal(), qlo.internal(), qhi.internal()));
954}
955
956template <typename T, typename U>
957 requires IsomorphicValues<T, U>
958constexpr auto ceil(const T &lhs, const U &rhs) {
959 auto qlhs = to_quantity(lhs);
960 auto qrhs = to_quantity(rhs);
961 if consteval {
962 return decltype(qlhs)(cevalm::ceil(qlhs.internal() / qrhs.internal()) *
963 qrhs.internal());
964 } else {
965 return decltype(qlhs)(std::ceil(qlhs.internal() / qrhs.internal()) *
966 qrhs.internal());
967 }
968}
969
970template <typename T, typename U>
971 requires IsomorphicValues<T, U>
972constexpr auto floor(const T &lhs, const U &rhs) {
973 auto qlhs = to_quantity(lhs);
974 auto qrhs = to_quantity(rhs);
975 if consteval {
976 return decltype(qlhs)(cevalm::floor(qlhs.internal() / qrhs.internal()) *
977 qrhs.internal());
978 } else {
979 return decltype(qlhs)(std::floor(qlhs.internal() / qrhs.internal()) *
980 qrhs.internal());
981 }
982}
983
984template <typename T, typename U>
985 requires IsomorphicValues<T, U>
986constexpr auto trunc(const T &lhs, const U &rhs) {
987 auto qlhs = to_quantity(lhs);
988 auto qrhs = to_quantity(rhs);
989 if consteval {
990 return decltype(qlhs)(cevalm::trunc(qlhs.internal() / qrhs.internal()) *
991 qrhs.internal());
992 } else {
993 return decltype(qlhs)(std::trunc(qlhs.internal() / qrhs.internal()) *
994 qrhs.internal());
995 }
996}
997
998template <typename T, typename U>
999 requires IsomorphicValues<T, U>
1000constexpr auto round(const T &lhs, const U &rhs) {
1001 auto qlhs = to_quantity(lhs);
1002 auto qrhs = to_quantity(rhs);
1003 if consteval {
1004 return decltype(qlhs)(cevalm::round(qlhs.internal() / qrhs.internal()) *
1005 qrhs.internal());
1006 } else {
1007 return decltype(qlhs)(std::round(qlhs.internal() / qrhs.internal()) *
1008 qrhs.internal());
1009 }
1010}
1011
1015template <typename Q>
1017 IsQuantity<Q> && std::ratio_equal_v<typename Q::mass, std::ratio<0>> &&
1018 std::ratio_equal_v<typename Q::current, std::ratio<0>> &&
1019 std::ratio_equal_v<typename Q::temperature, std::ratio<0>> &&
1020 std::ratio_equal_v<typename Q::luminosity, std::ratio<0>> &&
1021 std::ratio_equal_v<typename Q::moles, std::ratio<0>>;
1022
1027template <KinematicQuantity Q>
1028 requires std::ratio_equal_v<typename Q::angle, std::ratio<1>> &&
1029 std::ratio_equal_v<typename Q::length, std::ratio<0>>
1030constexpr auto to_linear(Q angular_distance, Length diameter) {
1031 return angular_distance * (diameter / 2.0) / rad;
1032}
1033
1038template <KinematicQuantity Q>
1039 requires std::ratio_equal_v<typename Q::length, std::ratio<1>> &&
1040 std::ratio_equal_v<typename Q::angle, std::ratio<0>>
1041constexpr auto to_angular(Q linear_distance, Length diameter) {
1042 return linear_distance / (diameter / 2.0) * rad;
1043}
1044
1045// Trig stuff
1046constexpr Number sin(Angle angle) {
1047 if consteval {
1048 return Number(cevalm::sin(angle.internal()));
1049 } else {
1050 return Number(std::sin(angle.internal()));
1051 }
1052}
1053
1054constexpr Number cos(Angle angle) {
1055 if consteval {
1056 return Number(cevalm::cos(angle.internal()));
1057 } else {
1058 return Number(std::cos(angle.internal()));
1059 }
1060}
1061
1062constexpr Number tan(Angle angle) {
1063 if consteval {
1064 return Number(cevalm::tan(angle.internal()));
1065 } else {
1066 return Number(std::tan(angle.internal()));
1067 }
1068}
1069
1070constexpr Angle asin(Number value) {
1071 if consteval {
1072 return Angle(cevalm::asin(value.internal()));
1073 } else {
1074 return Angle(std::asin(value.internal()));
1075 }
1076}
1077
1078constexpr Angle acos(Number value) {
1079 if consteval {
1080 return Angle(cevalm::acos(value.internal()));
1081 } else {
1082 return Angle(std::acos(value.internal()));
1083 }
1084}
1085
1086constexpr Angle atan(Number value) {
1087 if consteval {
1088 return Angle(cevalm::atan(value.internal()));
1089 } else {
1090 return Angle(std::atan(value.internal()));
1091 }
1092}
1093
1094template <typename T, typename U>
1095 requires IsomorphicValues<T, U>
1096constexpr Angle atan2(const T &y, const U &x) {
1097 auto qy = to_quantity(y);
1098 auto qx = to_quantity(x);
1099 if consteval {
1100 return Angle(cevalm::atan2(qy.internal(), qx.internal()));
1101 } else {
1102 return Angle(std::atan2(qy.internal(), qx.internal()));
1103 }
1104}
1105
1106// Angle wrapping
1107constexpr Angle wrap_positive(Angle angle) {
1108 Angle wrapped = mod(angle, rev);
1109 return wrapped < Angle(0) ? wrapped + rev : wrapped;
1110}
1111
1112constexpr Angle wrap_signed(Angle angle) {
1113 return wrap_positive(angle + rev / 2.0) - rev / 2.0;
1114}
1115
1116constexpr Angle shortest_difference(Angle start, Angle end) {
1117 return wrap_signed(end - start);
1118}
1119
1120} // namespace units
Definition units.h:25
constexpr Quantity()
Definition units.h:46
constexpr Self & operator*=(double scalar)
Definition units.h:146
constexpr Self & operator/=(double divisor)
Definition units.h:154
constexpr Quantity(double value)
Definition units.h:53
constexpr Self & operator+=(double other)
Definition units.h:117
constexpr double internal() const
Definition units.h:97
constexpr Self & operator-=(Self other)
Definition units.h:127
constexpr double to(Self unit) const
Definition units.h:103
constexpr Self & operator+=(Self other)
Definition units.h:108
constexpr Quantity(double value)
Definition units.h:62
constexpr Quantity(double value, Self unit)
Definition units.h:72
constexpr Self & operator=(const double &rhs)
Definition units.h:163
constexpr Self & operator-=(double other)
Definition units.h:136
constexpr Quantity(const Self &other)=default
Definition units.h:190
Definition units.h:204
Definition units.h:1016