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