Brick
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
14/**
15 * This class provides a physical unit conversion and dimensional analysis API.
16 * Quantities define a unit given the SI base dimensions. The dimensions are the exponents
17 * of each base, Mass, Length, Time, Current, Angle, Temperature, Luminosity, Moles.
18 * E.g. Velocity is length^1 * time^-1, length/time...
19 * Arithmetic and math functions can be used on Quantities like normal. Mass * Mass = Area.
20 */
21template <
22 typename Mass = std::ratio<0>, typename Length = std::ratio<0>, typename Time = std::ratio<0>,
23 typename Current = std::ratio<0>, typename Angle = std::ratio<0>,
24 typename Temperature = std::ratio<0>, typename Luminosity = std::ratio<0>,
25 typename Moles = std::ratio<0>>
26class Quantity {
27 protected:
28 double value;
29
30 public:
31 using mass = Mass;
32 using length = Length;
33 using time = Time;
34 using current = Current;
35 using angle = Angle;
36 using temperature = Temperature;
37 using luminosity = Luminosity;
38 using moles = Moles;
39
41 static constexpr bool is_dimensionless = std::is_same_v<Self, Quantity<>>;
42
43 /// Default to value of 0.
44 explicit constexpr Quantity() : value(0) {}
45
46 /**
47 * Construct a new Quantity object using the SI base unit
48 *
49 * @param value to initialize with
50 */
51 explicit constexpr Quantity(double value)
52 requires(!is_dimensionless)
53 : value(value) {}
54
55 /**
56 * Construct a new Quantity object
57 *
58 * @param value to initialize with
59 */
60 constexpr Quantity(double value)
61 requires is_dimensionless
62 : value(value) {}
63
64 /**
65 * Construct a quantity from a numeric value expressed in a specific unit.
66 *
67 * @param value numeric value in the provided unit
68 * @param unit unit in which value is expressed
69 */
70 constexpr Quantity(double value, Self unit) : value(value * unit.value) {}
71
72 /**
73 * Double cast overload, gets internal value.
74 *
75 * @return internal value
76 */
77 constexpr operator double() const
78 requires is_dimensionless
79 {
80 return value;
81 }
82
83 /**
84 * Constructor copying from a quantity with the same dimensions
85 *
86 * @param other the quantity to copy
87 */
88 constexpr Quantity(const Self& other) = default;
89
90 /**
91 * Gets the internal raw value
92 *
93 * @return constexpr internal value
94 */
95 constexpr double internal() const { return value; }
96
97 /**
98 * Gets this quantity's numeric value expressed in a specific unit.
99 * e.g. double v_mps = v.to(mps);
100 */
101 constexpr double to(Self unit) const { return value / unit.value; }
102
103 /// Adds another quantity's value to this.
104 constexpr Self& operator+=(Self other) {
105 value += other.value;
106 return *this;
107 }
108
109 /**
110 * Adds a double to this internal value
111 * Only allowed if this quantity is dimensionless
112 */
113 constexpr Self& operator+=(double other)
114 requires is_dimensionless
115 {
116 value += other;
117 return *this;
118 }
119
120 /// Subtracts another quantity's value from this.
121 constexpr Self& operator-=(Self other) {
122 value -= other.value;
123 return *this;
124 }
125
126 /**
127 * Subtracts a double from this internal value
128 * Only allowed if this quantity is dimensionless
129 */
130 constexpr Self& operator-=(double other)
131 requires is_dimensionless
132 {
133 value -= other;
134 return *this;
135 }
136
137 /// Multiplies this quantity by a scalar
138 constexpr Self& operator*=(double scalar) {
139 value *= scalar;
140 return *this;
141 }
142
143 /// Divides this quantity by a scalar
144 constexpr Self& operator/=(double divisor) {
145 value /= divisor;
146 return *this;
147 }
148
149 /**
150 * Sets this quantity's value to another value
151 * Only allowed if the quantity is dimensionless
152 */
153 constexpr Self& operator=(const double& rhs)
154 requires is_dimensionless
155 {
156 value = rhs;
157 return *this;
158 }
159};
160
161/// Number is just dimensionless
163
164/// quantity checker. Used by the is_quantity concept
165template <
166 typename Mass = std::ratio<0>, typename Length = std::ratio<0>, typename Time = std::ratio<0>,
167 typename Current = std::ratio<0>, typename Angle = std::ratio<0>,
168 typename Temperature = std::ratio<0>, typename Luminosity = std::ratio<0>,
169 typename Moles = std::ratio<0>>
173
174/// Used to require a Quantity be passed in a template (instead of typename Q)
175template <typename Q>
176concept IsQuantity = requires(Q q) { quantity_checker(q); };
177
178/**
179 * Creates a quantity from a numeric value expressed in a specific unit.
180 * e.g. Velocity v = from(5, inps);
181 */
182template <IsQuantity Q>
183constexpr Q from(double value, Q unit) {
184 return Q(value * unit.internal());
185}
186
187/// Isomorphic concept checks whether dimensions are the same between quantities
188template <typename Q, typename... Quantities>
189concept Isomorphic =
190 ((std::convertible_to<Q, Quantities> && std::convertible_to<Quantities, Q>) && ...);
191
192/// Multiplying quantities adds their dimensions
193template <IsQuantity Q1, IsQuantity Q2>
195 std::ratio_add<typename Q1::mass, typename Q2::mass>,
196 std::ratio_add<typename Q1::length, typename Q2::length>,
197 std::ratio_add<typename Q1::time, typename Q2::time>,
198 std::ratio_add<typename Q1::current, typename Q2::current>,
199 std::ratio_add<typename Q1::angle, typename Q2::angle>,
200 std::ratio_add<typename Q1::temperature, typename Q2::temperature>,
201 std::ratio_add<typename Q1::luminosity, typename Q2::luminosity>,
202 std::ratio_add<typename Q1::moles, typename Q2::moles>>;
203
204/// Dividing quantities subtracts their dimensions
205template <IsQuantity Q1, IsQuantity Q2>
207 std::ratio_subtract<typename Q1::mass, typename Q2::mass>,
208 std::ratio_subtract<typename Q1::length, typename Q2::length>,
209 std::ratio_subtract<typename Q1::time, typename Q2::time>,
210 std::ratio_subtract<typename Q1::current, typename Q2::current>,
211 std::ratio_subtract<typename Q1::angle, typename Q2::angle>,
212 std::ratio_subtract<typename Q1::temperature, typename Q2::temperature>,
213 std::ratio_subtract<typename Q1::luminosity, typename Q2::luminosity>,
214 std::ratio_subtract<typename Q1::moles, typename Q2::moles>>;
215
216/// Exponentiating a quantity multiplies its dimensions by the power
217template <IsQuantity Q, typename factor>
219 std::ratio_multiply<typename Q::mass, factor>, std::ratio_multiply<typename Q::length, factor>,
220 std::ratio_multiply<typename Q::time, factor>, std::ratio_multiply<typename Q::current, factor>,
221 std::ratio_multiply<typename Q::angle, factor>,
222 std::ratio_multiply<typename Q::temperature, factor>,
223 std::ratio_multiply<typename Q::luminosity, factor>,
224 std::ratio_multiply<typename Q::moles, factor>>;
225
226/// Rooting a quantity divides its dimensions by the root
227template <IsQuantity Q, typename quotient>
229 std::ratio_divide<typename Q::mass, quotient>, std::ratio_divide<typename Q::length, quotient>,
230 std::ratio_divide<typename Q::time, quotient>, std::ratio_divide<typename Q::current, quotient>,
231 std::ratio_divide<typename Q::angle, quotient>,
232 std::ratio_divide<typename Q::temperature, quotient>,
233 std::ratio_divide<typename Q::luminosity, quotient>,
234 std::ratio_divide<typename Q::moles, quotient>>;
235
236template <IsQuantity Q>
237constexpr Q operator+(Q rhs) {
238 return rhs;
239}
240
241/// Add two isomorphic quantities
242template <IsQuantity Q, IsQuantity R>
243constexpr Q operator+(Q lhs, R rhs)
244 requires Isomorphic<Q, R>
245{
246 return Q(lhs.internal() + rhs.internal());
247}
248
249/// Negate a quantity
250template <IsQuantity Q>
251constexpr Q operator-(Q rhs) {
252 return Q(-rhs.internal());
253}
254
255/// Subtract two isomorphic quantities
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
263/// Multiply a Quantity by a Number
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
270/// Multiply a Quantity by a Number
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
277/// Divide a Quantity by a Number
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
284/// Divide a Number by a Quantity
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
291/// Multiply two Quantities
292template <IsQuantity Q1, IsQuantity Q2>
293constexpr auto operator*(Q1 lhs, Q2 rhs) {
294 return Multiplied<Q1, Q2>(lhs.internal() * rhs.internal());
295}
296
297/// Divide two Quantities
298template <IsQuantity Q1, IsQuantity Q2>
299constexpr auto operator/(Q1 lhs, Q2 rhs) {
300 return Divided<Q1, Q2>(lhs.internal() / rhs.internal());
301}
302
303/// Check whether two Quantities are equal
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
311/// Check whether two Quantities are not equal
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
319/// Check whether a Quantity is less than or equal to another
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
327/// Check whether a Quantity is greater than or equal to another
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
335/// Check whether a Quantity is less than another
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
343/// Check whether a Quantity is greater than another
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
351/**
352 * Macro that defines a new unit as a specific Quantity type
353 *
354 * @param Name name for the unit (e.g. Length)
355 * @param long_suffix long suffix for the unit (e.g. meters)
356 * @param suffix alternate short suffix for the unit (e.g. m)
357 * @param m mass dim
358 * @param l length dim
359 * @param t time dim
360 * @param i current dim
361 * @param a angle dim
362 * @param o temperature dim
363 * @param j luminosity dim
364 * @param n moles dim
365 */
366#define NEW_UNIT(Name, long_suffix, suffix, m, l, t, i, a, o, j, n) \
367 using Name = Quantity< \
368 std::ratio<m>, std::ratio<l>, std::ratio<t>, std::ratio<i>, std::ratio<a>, std::ratio<o>, \
369 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< \
377 std::ratio<m>, std::ratio<l>, std::ratio<t>, std::ratio<i>, std::ratio<a>, \
378 std::ratio<o>, std::ratio<j>, std::ratio<n>>(static_cast<double>(value)) \
379 ); \
380 } \
381 constexpr Name operator""_##long_suffix(unsigned long long value) { \
382 return Name( \
383 Quantity< \
384 std::ratio<m>, std::ratio<l>, std::ratio<t>, std::ratio<i>, std::ratio<a>, \
385 std::ratio<o>, std::ratio<j>, std::ratio<n>>(static_cast<double>(value)) \
386 ); \
387 } \
388 constexpr Name operator""_##suffix(long double value) { \
389 return Name( \
390 Quantity< \
391 std::ratio<m>, std::ratio<l>, std::ratio<t>, std::ratio<i>, std::ratio<a>, \
392 std::ratio<o>, std::ratio<j>, std::ratio<n>>(static_cast<double>(value)) \
393 ); \
394 } \
395 constexpr Name operator""_##suffix(unsigned long long value) { \
396 return Name( \
397 Quantity< \
398 std::ratio<m>, std::ratio<l>, std::ratio<t>, std::ratio<i>, std::ratio<a>, \
399 std::ratio<o>, std::ratio<j>, std::ratio<n>>(static_cast<double>(value)) \
400 ); \
401 } \
402 }
403
404#define NEW_UNIT_LITERAL(Name, long_suffix, suffix, multiple) \
405 [[maybe_unused]] \
406 constexpr Name suffix = multiple; \
407 constexpr Name long_suffix = multiple; \
408 namespace literals { \
409 constexpr Name operator""_##long_suffix(long double value) { \
410 return static_cast<double>(value) * multiple; \
411 } \
412 constexpr Name operator""_##suffix(long double value) { \
413 return static_cast<double>(value) * multiple; \
414 } \
415 constexpr Name operator""_##long_suffix(unsigned long long value) { \
416 return static_cast<double>(value) * multiple; \
417 } \
418 constexpr Name operator""_##suffix(unsigned long long value) { \
419 return static_cast<double>(value) * multiple; \
420 } \
421 }
422
423#define NEW_METRIC_PREFIXES(Name, long_base, base) \
424 NEW_UNIT_LITERAL(Name, tera##long_base, T##base, base * 1E12) \
425 NEW_UNIT_LITERAL(Name, giga##long_base, G##base, base * 1E9) \
426 NEW_UNIT_LITERAL(Name, mega##long_base, M##base, base * 1E6) \
427 NEW_UNIT_LITERAL(Name, kilo##long_base, k##base, base * 1E3) \
428 NEW_UNIT_LITERAL(Name, centi##long_base, c##base, base / 1E2) \
429 NEW_UNIT_LITERAL(Name, milli##long_base, m##base, base / 1E3) \
430 NEW_UNIT_LITERAL(Name, micro##long_base, u##base, base / 1E6) \
431 NEW_UNIT_LITERAL(Name, nano##long_base, n##base, base / 1E9) \
432 NEW_UNIT_LITERAL(Name, pico##long_base, p##base, base / 1E12)
433
434NEW_UNIT(Mass, kilograms, kg, 1, 0, 0, 0, 0, 0, 0, 0)
435NEW_UNIT_LITERAL(Mass, grams, g, kg / 1000)
436NEW_UNIT_LITERAL(Mass, ounces, oz, g * 28.349523125)
437NEW_UNIT_LITERAL(Mass, pounds_mass, lbm, oz * 16)
438NEW_UNIT_LITERAL(Mass, tons, ton, lbm * 2000)
439
440NEW_UNIT(Time, seconds, s, 0, 0, 1, 0, 0, 0, 0, 0)
441NEW_METRIC_PREFIXES(Time, seconds, s)
442NEW_UNIT_LITERAL(Time, minutes, mins, s * 60)
443NEW_UNIT_LITERAL(Time, hours, hr, mins * 60)
444NEW_UNIT_LITERAL(Time, days, day, hr * 24)
445
446NEW_UNIT(Length, meters, m, 0, 1, 0, 0, 0, 0, 0, 0)
447NEW_METRIC_PREFIXES(Length, meters, m)
448NEW_UNIT_LITERAL(Length, inches, in, cm * 2.54)
449NEW_UNIT_LITERAL(Length, feet, ft, in * 12)
450NEW_UNIT_LITERAL(Length, yards, yd, ft * 3)
451NEW_UNIT_LITERAL(Length, miles, mi, ft * 5280)
452/// Tile is the measured length of a vex field tile in real life
453NEW_UNIT_LITERAL(Length, tiles, tile, in * 23.75)
454NEW_UNIT_LITERAL(Length, football_fields, football_field, yd * 100)
455NEW_UNIT_LITERAL(Length, big_macs, big_mac, 9 * cm)
456
457NEW_UNIT(Area, square_meters, m2, 0, 2, 0, 0, 0, 0, 0, 0)
458NEW_UNIT_LITERAL(Area, square_yards, yd2, yd* yd)
459NEW_UNIT_LITERAL(Area, square_feet, ft2, ft* ft)
460NEW_UNIT_LITERAL(Area, square_inches, in2, in* in)
461NEW_UNIT_LITERAL(Area, acres, acre, yd2 * 4840)
462
463NEW_UNIT(Volume, cubic_meters, m3, 0, 3, 0, 0, 0, 0, 0, 0)
464NEW_UNIT_LITERAL(Volume, cubic_feet, ft3, ft * ft * ft)
465NEW_UNIT_LITERAL(Volume, cubic_inches, in3, in * in * in)
466NEW_UNIT_LITERAL(Volume, liters, liter, m3 * 0.001)
467NEW_UNIT_LITERAL(Volume, gallons, gal, in3 * 231)
468
469NEW_UNIT(Velocity, meters_per_second, mps, 0, 1, -1, 0, 0, 0, 0, 0)
470NEW_METRIC_PREFIXES(Velocity, mps, mps)
471NEW_UNIT_LITERAL(Velocity, inches_per_second, inps, in / s)
472NEW_UNIT_LITERAL(Velocity, feet_per_second, ftps, ft / s)
473NEW_UNIT_LITERAL(Velocity, miles_per_hour, miph, mi / hr)
474
475NEW_UNIT(Acceleration, meters_per_second_squared, mps2, 0, 1, -2, 0, 0, 0, 0, 0)
476NEW_METRIC_PREFIXES(Acceleration, mps2, mps2)
477NEW_UNIT_LITERAL(Acceleration, inches_per_second_squared, inps2, in / (s * s))
478
479NEW_UNIT(Jerk, meters_per_second_cubed, mps3, 0, 1, -3, 0, 0, 0, 0, 0)
480NEW_METRIC_PREFIXES(Jerk, mps3, mps3)
481NEW_UNIT_LITERAL(Jerk, inches_per_second_cubed, inps3, in / (s * s * s))
482
483NEW_UNIT(Absement, meter_seconds, m_s, 0, 1, 1, 0, 0, 0, 0, 0)
484NEW_UNIT_LITERAL(Absement, inch_seconds, in_s, in* s)
485
486NEW_UNIT(Angle, radians, rad, 0, 0, 0, 0, 1, 0, 0, 0)
487NEW_UNIT_LITERAL(Angle, degrees, deg, rad* std::numbers::pi / 180)
488NEW_UNIT_LITERAL(Angle, revolutions, rev, rad* std::numbers::pi * 2)
489NEW_UNIT_LITERAL(Angle, gradians, grad, deg * 0.9)
490
491NEW_UNIT(AngularVelocity, radians_per_second, radps, 0, 0, -1, 0, 1, 0, 0, 0)
492NEW_UNIT_LITERAL(AngularVelocity, degrees_per_second, dps, deg / s)
493NEW_UNIT_LITERAL(AngularVelocity, revolutions_per_second, revps, rev / s)
494NEW_UNIT_LITERAL(AngularVelocity, revolutions_per_minute, rpm, rev / mins)
495
496NEW_UNIT(AngularAcceleration, radians_per_second_squared, radps2, 0, 0, -2, 0, 1, 0, 0, 0)
497NEW_UNIT_LITERAL(AngularAcceleration, degrees_per_second_squared, dps2, deg / (s * s))
498NEW_UNIT_LITERAL(AngularAcceleration, revolutions_per_second_squared, revps2, rev / (s * s))
499
500NEW_UNIT(AngularJerk, radians_per_second_cubed, radps3, 0, 0, -3, 0, 1, 0, 0, 0)
501NEW_UNIT_LITERAL(AngularJerk, degrees_per_second_cubed, dps3, deg / (s * s * s))
502NEW_UNIT_LITERAL(AngularJerk, revolutions_per_second_cubed, revps3, 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, -1, 0, 0, 0, 0)
568NEW_UNIT_LITERAL(LinearVelocityFeedforward, volts_per_inch_per_second, VpInps, V / inps)
569
570NEW_UNIT(LinearProportionalGain, volts_per_meter, VpM, 1, 1, -3, -1, 0, 0, 0, 0)
571NEW_UNIT_LITERAL(LinearProportionalGain, volts_per_inch, VpIn, V / in)
572
573NEW_UNIT(LinearIntegralGain, volts_per_meter_second, VpMS, 1, 1, -4, -1, 0, 0, 0, 0)
574NEW_UNIT_LITERAL(LinearIntegralGain, volts_per_inch_second, VpInS, V / (in * s))
575
577 LinearAccelerationFeedforward, volts_per_meter_per_second_squared, VpMps2, 1, 1, -1, -1, 0, 0,
578 0, 0
579)
580NEW_UNIT_LITERAL(
581 LinearAccelerationFeedforward, volts_per_inch_per_second_squared, VpInps2, V / inps2
582)
583
584NEW_UNIT(LinearDerivativeGain, volt_seconds_per_meter, VspM, 1, 1, -2, -1, 0, 0, 0, 0)
585NEW_UNIT_LITERAL(LinearDerivativeGain, volt_seconds_per_inch, VspIn, V / inps)
586
587/*
588 * These are the same dimension, and we don't have special names, so "using" is
589 * cleanest
590 */
591using LinearVelocityProportionalGain = LinearDerivativeGain;
592
594 LinearVelocityDerivativeGain, volt_seconds_squared_per_meter, Vs2pM, 1, 1, -1, -1, 0, 0, 0, 0
595)
596NEW_UNIT_LITERAL(LinearVelocityDerivativeGain, volt_seconds_squared_per_inch, Vs2pIn, V / inps2)
597
598using LinearVelocityIntegralGain = LinearProportionalGain;
599
601 AngularVelocityFeedforward, volts_per_radian_per_second, VpRadPs, 1, 2, -2, -1, -1, 0, 0, 0
602)
603NEW_UNIT_LITERAL(AngularVelocityFeedforward, volts_per_degree_per_second, VpDegPs, V / dps)
604
605NEW_UNIT(AngularProportionalGain, volts_per_radian, VpRad, 1, 2, -3, -1, -1, 0, 0, 0)
606NEW_UNIT_LITERAL(AngularProportionalGain, volts_per_degree, VpDeg, V / deg)
607
608NEW_UNIT(AngularIntegralGain, volts_per_radian_second, VpRadS, 1, 2, -4, -1, -1, 0, 0, 0)
609NEW_UNIT_LITERAL(AngularIntegralGain, volts_per_degree_second, VpDegS, V / (deg * s))
610
612 AngularAccelerationFeedforward, volts_per_radian_per_second_squared, VpRadPs2, 1, 2, -1, -1, -1,
613 0, 0, 0
614)
615NEW_UNIT_LITERAL(
616 AngularAccelerationFeedforward, volts_per_degree_per_second_squared, VpDegPs2, V / dps2
617)
618
619NEW_UNIT(AngularDerivativeGain, volt_seconds_per_radian, VspRad, 1, 2, -2, -1, -1, 0, 0, 0)
620NEW_UNIT_LITERAL(AngularDerivativeGain, volt_seconds_per_degree, VspDeg, V / dps)
621
622using AngularVelocityProportionalGain = AngularDerivativeGain;
623
625 AngularVelocityDerivativeGain, volt_seconds_squared_per_radian, Vs2pRad, 1, 2, -1, -1, -1, 0, 0,
626 0
627)
628NEW_UNIT_LITERAL(AngularVelocityDerivativeGain, volt_seconds_squared_per_degree, Vs2pDeg, V / dps2)
629
630using AngularVelocityIntegralGain = AngularProportionalGain;
631
632#undef NEW_METRIC_PREFIXES
633#undef NEW_UNIT_LITERAL
634#undef NEW_UNIT
635
636/*
637 * Temperature gets special treatment since its conversions are affine.
638 * Addition subtraction and negation are all just not allowed. Do it manually.
639 */
640using TemperatureQuantity = Quantity<
641 std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>,
642 std::ratio<0>, std::ratio<0>>;
643
644enum class TemperatureUnit { Kelvin, Celsius, Fahrenheit };
645
646class Temperature : public TemperatureQuantity {
647 public:
648 explicit constexpr Temperature() = default;
649 explicit constexpr Temperature(double kelvins) : TemperatureQuantity(kelvins) {}
650
651 constexpr double to(TemperatureUnit unit) const;
652
653 constexpr Temperature& operator+=(Temperature) = delete;
654 constexpr Temperature& operator-=(Temperature) = delete;
655};
656
657constexpr TemperatureUnit kelvin = TemperatureUnit::Kelvin;
658constexpr TemperatureUnit K = kelvin;
659constexpr TemperatureUnit celsius = TemperatureUnit::Celsius;
660constexpr TemperatureUnit fahrenheit = TemperatureUnit::Fahrenheit;
661
662constexpr Temperature from(double value, TemperatureUnit unit) {
663 switch (unit) {
664 case TemperatureUnit::Kelvin:
665 return Temperature(value);
666 case TemperatureUnit::Celsius:
667 return Temperature(value + 273.15);
668 case TemperatureUnit::Fahrenheit:
669 return Temperature((value - 32.0) * (5.0 / 9.0) + 273.15);
670 }
671 return Temperature(value);
672}
673
674constexpr double Temperature::to(TemperatureUnit unit) const {
675 switch (unit) {
676 case TemperatureUnit::Kelvin:
677 return internal();
678 case TemperatureUnit::Celsius:
679 return internal() - 273.15;
680 case TemperatureUnit::Fahrenheit:
681 return (internal() - 273.15) * (9.0 / 5.0) + 32.0;
682 }
683 return internal();
684}
685
686constexpr Temperature operator+(Temperature) = delete;
687constexpr Temperature operator-(Temperature) = delete;
688constexpr Temperature operator+(Temperature, Temperature) = delete;
689constexpr Temperature operator-(Temperature, Temperature) = delete;
690
691namespace literals {
692
693constexpr Temperature operator""_kelvin(long double value) {
694 return from(static_cast<double>(value), kelvin);
695}
696
697constexpr Temperature operator""_kelvin(unsigned long long value) {
698 return from(static_cast<double>(value), kelvin);
699}
700
701constexpr Temperature operator""_K(long double value) {
702 return from(static_cast<double>(value), kelvin);
703}
704
705constexpr Temperature operator""_K(unsigned long long value) {
706 return from(static_cast<double>(value), kelvin);
707}
708
709constexpr Temperature operator""_celsius(long double value) {
710 return from(static_cast<double>(value), celsius);
711}
712
713constexpr Temperature operator""_celsius(unsigned long long value) {
714 return from(static_cast<double>(value), celsius);
715}
716
717constexpr Temperature operator""_fahrenheit(long double value) {
718 return from(static_cast<double>(value), fahrenheit);
719}
720
721constexpr Temperature operator""_fahrenheit(unsigned long long value) {
722 return from(static_cast<double>(value), fahrenheit);
723}
724
725} // namespace literals
726
727/**
728 * Helper that converts arithmetic types to Number so they can be used in the
729 * following
730 */
731template <typename T>
732constexpr auto to_quantity(T value) -> std::conditional_t<std::is_arithmetic_v<T>, Number, T> {
733 if constexpr (std::is_arithmetic_v<T>)
734 return Number(value);
735 else
736 return value;
737}
738
739template <typename T>
740using quantity_type = decltype(to_quantity(std::declval<T>()));
741
742template <typename T, typename... U>
743concept IsomorphicValues = Isomorphic<quantity_type<T>, quantity_type<U>...>;
744
745/*
746 * Using this first function as an example, the "if consteval" checks whether
747 * it is being executed at compile time or runtime.
748 * cevalm::abs is consteval, std::abs is runtime only. We prefer std::abs at
749 * runtime since it uses libm (or compiler intrinsics) for this specific cpu
750 * rather than cevalm which reimplements fdlibm manually.
751 */
752template <typename T>
753constexpr auto abs(const T& lhs) {
754 auto q = to_quantity(lhs);
755 using Q = decltype(q);
756 if consteval {
757 return Q(cevalm::abs(q.internal()));
758 } else {
759 return Q(std::abs(q.internal()));
760 }
761}
762
763template <typename T, typename U>
765constexpr auto max(const T& lhs, const U& rhs) {
766 auto qlhs = to_quantity(lhs);
767 auto qrhs = to_quantity(rhs);
768 return (qlhs > qrhs ? qlhs : qrhs);
769}
770
771template <typename T, typename U>
772 requires IsomorphicValues<T, U>
773constexpr auto min(const T& lhs, const U& rhs) {
774 auto qlhs = to_quantity(lhs);
775 auto qrhs = to_quantity(rhs);
776 return (qlhs < qrhs ? qlhs : qrhs);
777}
778
779template <typename T>
780constexpr auto sgn(const T& lhs) {
781 auto q = to_quantity(lhs);
782 if (q.internal() > 0) return Number(1);
783 if (q.internal() < 0) return Number(-1);
784 return Number(0);
785}
786
787template <int R, typename T>
789constexpr auto pow(const T& lhs) {
790 if constexpr (R == 0) {
791 return Number(1.0);
792 } else {
793 auto q = to_quantity(lhs);
794 using Q = decltype(q);
796 if consteval {
797 return S(cevalm::pow(q.internal(), R));
798 } else {
799 return S(std::pow(q.internal(), R));
800 }
801 }
802}
803
804template <typename T>
805constexpr auto square(const T& lhs) {
806 return lhs * lhs;
807}
808
809template <typename T>
810constexpr 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>
852constexpr auto sqrt(const T& lhs) {
853 return root<2>(lhs);
854}
855
856template <typename T>
857constexpr auto cbrt(const T& lhs) {
858 return root<3>(lhs);
859}
860
861template <typename T, typename U>
863constexpr auto hypot(const T& lhs, const U& rhs) {
864 auto qlhs = to_quantity(lhs);
865 auto qrhs = to_quantity(rhs);
866 if consteval {
867 return decltype(qlhs)(cevalm::hypot(qlhs.internal(), qrhs.internal()));
868 } else {
869 return decltype(qlhs)(std::hypot(qlhs.internal(), qrhs.internal()));
870 }
871}
872
873template <typename T, typename U>
875constexpr auto mod(const T& lhs, const U& rhs) {
876 auto qlhs = to_quantity(lhs);
877 auto qrhs = to_quantity(rhs);
878 if consteval {
879 return decltype(qlhs)(cevalm::fmod(qlhs.internal(), qrhs.internal()));
880 } else {
881 return decltype(qlhs)(std::fmod(qlhs.internal(), qrhs.internal()));
882 }
883}
884
885template <typename T, typename U>
886constexpr auto copysign(const T& lhs, const U& rhs) {
887 auto qlhs = to_quantity(lhs);
888 auto qrhs = to_quantity(rhs);
889 if consteval {
890 return decltype(qlhs)(cevalm::copysign(qlhs.internal(), qrhs.internal()));
891 } else {
892 return decltype(qlhs)(std::copysign(qlhs.internal(), qrhs.internal()));
893 }
894}
895
896template <typename T>
897constexpr auto signbit(const T& lhs) {
898 auto q = to_quantity(lhs);
899 return std::signbit(q.internal());
900}
901
902template <typename T, typename U, typename V>
904constexpr auto clamp(const T& lhs, const U& lo, const V& hi) {
905 auto qlhs = to_quantity(lhs);
906 auto qlo = to_quantity(lo);
907 auto qhi = to_quantity(hi);
908 return decltype(qlhs)(std::clamp(qlhs.internal(), qlo.internal(), qhi.internal()));
909}
910
911template <typename T, typename U>
913constexpr auto ceil(const T& lhs, const U& rhs) {
914 auto qlhs = to_quantity(lhs);
915 auto qrhs = to_quantity(rhs);
916 if consteval {
917 return decltype(qlhs)(cevalm::ceil(qlhs.internal() / qrhs.internal()) * qrhs.internal());
918 } else {
919 return decltype(qlhs)(std::ceil(qlhs.internal() / qrhs.internal()) * qrhs.internal());
920 }
921}
922
923template <typename T, typename U>
925constexpr auto floor(const T& lhs, const U& rhs) {
926 auto qlhs = to_quantity(lhs);
927 auto qrhs = to_quantity(rhs);
928 if consteval {
929 return decltype(qlhs)(cevalm::floor(qlhs.internal() / qrhs.internal()) * qrhs.internal());
930 } else {
931 return decltype(qlhs)(std::floor(qlhs.internal() / qrhs.internal()) * qrhs.internal());
932 }
933}
934
935template <typename T, typename U>
937constexpr auto trunc(const T& lhs, const U& rhs) {
938 auto qlhs = to_quantity(lhs);
939 auto qrhs = to_quantity(rhs);
940 if consteval {
941 return decltype(qlhs)(cevalm::trunc(qlhs.internal() / qrhs.internal()) * qrhs.internal());
942 } else {
943 return decltype(qlhs)(std::trunc(qlhs.internal() / qrhs.internal()) * qrhs.internal());
944 }
945}
946
947template <typename T, typename 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()) * qrhs.internal());
954 } else {
955 return decltype(qlhs)(std::round(qlhs.internal() / qrhs.internal()) * qrhs.internal());
956 }
957}
958
959/// Only allows nonzero length, time, angle for the next two functions
960template <typename Q>
961concept KinematicQuantity = IsQuantity<Q> && std::ratio_equal_v<typename Q::mass, std::ratio<0>> &&
962 std::ratio_equal_v<typename Q::current, std::ratio<0>> &&
963 std::ratio_equal_v<typename Q::temperature, std::ratio<0>> &&
964 std::ratio_equal_v<typename Q::luminosity, std::ratio<0>> &&
965 std::ratio_equal_v<typename Q::moles, std::ratio<0>>;
966
967/**
968 * Helper to go from angular to linear, like from encoder rotation to wheel
969 * distance travel
970 */
971template <KinematicQuantity Q>
972 requires std::ratio_equal_v<typename Q::angle, std::ratio<1>> &&
973 std::ratio_equal_v<typename Q::length, std::ratio<0>>
974constexpr auto to_linear(Q angular_distance, Length diameter) {
975 return angular_distance * (diameter / 2.0) / rad;
976}
977
978/**
979 * Helper to go from linear to angular, like from wheel distance travel to wheel
980 * rotation
981 */
982template <KinematicQuantity Q>
983 requires std::ratio_equal_v<typename Q::length, std::ratio<1>> &&
984 std::ratio_equal_v<typename Q::angle, std::ratio<0>>
985constexpr auto to_angular(Q linear_distance, Length diameter) {
986 return linear_distance / (diameter / 2.0) * rad;
987}
988
989// Trig stuff
990constexpr Number sin(Angle angle) {
991 if consteval {
992 return Number(cevalm::sin(angle.internal()));
993 } else {
994 return Number(std::sin(angle.internal()));
995 }
996}
997
998constexpr Number cos(Angle angle) {
999 if consteval {
1000 return Number(cevalm::cos(angle.internal()));
1001 } else {
1002 return Number(std::cos(angle.internal()));
1003 }
1004}
1005
1006constexpr Number tan(Angle angle) {
1007 if consteval {
1008 return Number(cevalm::tan(angle.internal()));
1009 } else {
1010 return Number(std::tan(angle.internal()));
1011 }
1012}
1013
1014constexpr Angle asin(Number value) {
1015 if consteval {
1016 return Angle(cevalm::asin(value.internal()));
1017 } else {
1018 return Angle(std::asin(value.internal()));
1019 }
1020}
1021
1022constexpr Angle acos(Number value) {
1023 if consteval {
1024 return Angle(cevalm::acos(value.internal()));
1025 } else {
1026 return Angle(std::acos(value.internal()));
1027 }
1028}
1029
1030constexpr Angle atan(Number value) {
1031 if consteval {
1032 return Angle(cevalm::atan(value.internal()));
1033 } else {
1034 return Angle(std::atan(value.internal()));
1035 }
1036}
1037
1038template <typename T, typename U>
1039 requires IsomorphicValues<T, U>
1040constexpr Angle atan2(const T& y, const U& x) {
1041 auto qy = to_quantity(y);
1042 auto qx = to_quantity(x);
1043 if consteval {
1044 return Angle(cevalm::atan2(qy.internal(), qx.internal()));
1045 } else {
1046 return Angle(std::atan2(qy.internal(), qx.internal()));
1047 }
1048}
1049
1050// Angle wrapping
1051constexpr Angle wrap_positive(Angle angle) {
1052 Angle wrapped = mod(angle, rev);
1053 return wrapped < Angle(0) ? wrapped + rev : wrapped;
1054}
1055
1056constexpr Angle wrap_signed(Angle angle) { return wrap_positive(angle + rev / 2.0) - rev / 2.0; }
1057
1058constexpr Angle shortest_difference(Angle start, Angle end) { return wrap_signed(end - start); }
1059
1060} // namespace units
Definition units.h:26
constexpr Quantity()
Default to value of 0.
Definition units.h:44
constexpr Self & operator*=(double scalar)
Multiplies this quantity by a scalar.
Definition units.h:138
constexpr Self & operator/=(double divisor)
Divides this quantity by a scalar.
Definition units.h:144
constexpr Quantity(double value)
Definition units.h:51
constexpr Self & operator+=(double other)
Definition units.h:113
constexpr double internal() const
Definition units.h:95
constexpr Self & operator-=(Self other)
Subtracts another quantity's value from this.
Definition units.h:121
constexpr double to(Self unit) const
Definition units.h:101
constexpr Self & operator+=(Self other)
Adds another quantity's value to this.
Definition units.h:104
constexpr Quantity(double value)
Definition units.h:60
constexpr Quantity(double value, Self unit)
Definition units.h:70
constexpr Self & operator=(const double &rhs)
Definition units.h:153
constexpr Self & operator-=(double other)
Definition units.h:130
constexpr Quantity(const Self &other)=default
Definition units.h:646
Used to require a Quantity be passed in a template (instead of typename Q).
Definition units.h:176
Definition units.h:743
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:961
Definition units.h:691
Definition units.h:12
constexpr bool operator==(const Q &lhs, const R &rhs)
Check whether two Quantities are equal.
Definition units.h:305
constexpr Q operator/(Q quantity, Number divisor)
Divide a Quantity by a Number.
Definition units.h:280
constexpr Q operator-(Q rhs)
Negate a quantity.
Definition units.h:251
constexpr bool operator!=(const Q &lhs, const R &rhs)
Check whether two Quantities are not equal.
Definition units.h:313
constexpr bool operator>=(const Q &lhs, const R &rhs)
Check whether a Quantity is greater than or equal to another.
Definition units.h:329
constexpr Q from(double value, Q unit)
Definition units.h:183
Quantity< std::ratio_multiply< typename Q::mass, factor >, std::ratio_multiply< typename Q::length, factor >, std::ratio_multiply< typename Q::time, factor >, std::ratio_multiply< typename Q::current, factor >, std::ratio_multiply< typename Q::angle, factor >, std::ratio_multiply< typename Q::temperature, factor >, std::ratio_multiply< typename Q::luminosity, factor >, std::ratio_multiply< typename Q::moles, factor > > Exponentiated
Exponentiating a quantity multiplies its dimensions by the power.
Definition units.h:218
constexpr auto to_quantity(T value) -> std::conditional_t< std::is_arithmetic_v< T >, Number, T >
Definition units.h:732
NEW_UNIT(LinearAccelerationFeedforward, volts_per_meter_per_second_squared, VpMps2, 1, 1, -1, -1, 0, 0, 0, 0) NEW_UNIT_LITERAL(LinearAccelerationFeedforward
Tile is the measured length of a vex field tile in real life.
constexpr auto to_linear(Q angular_distance, Length diameter)
Definition units.h:974
Quantity< std::ratio_divide< typename Q::mass, quotient >, std::ratio_divide< typename Q::length, quotient >, std::ratio_divide< typename Q::time, quotient >, std::ratio_divide< typename Q::current, quotient >, std::ratio_divide< typename Q::angle, quotient >, std::ratio_divide< typename Q::temperature, quotient >, std::ratio_divide< typename Q::luminosity, quotient >, std::ratio_divide< typename Q::moles, quotient > > Rooted
Rooting a quantity divides its dimensions by the root.
Definition units.h:228
Quantity< std::ratio_subtract< typename Q1::mass, typename Q2::mass >, std::ratio_subtract< typename Q1::length, typename Q2::length >, std::ratio_subtract< typename Q1::time, typename Q2::time >, std::ratio_subtract< typename Q1::current, typename Q2::current >, std::ratio_subtract< typename Q1::angle, typename Q2::angle >, std::ratio_subtract< typename Q1::temperature, typename Q2::temperature >, std::ratio_subtract< typename Q1::luminosity, typename Q2::luminosity >, std::ratio_subtract< typename Q1::moles, typename Q2::moles > > Divided
Dividing quantities subtracts their dimensions.
Definition units.h:206
constexpr bool operator<=(const Q &lhs, const R &rhs)
Check whether a Quantity is less than or equal to another.
Definition units.h:321
constexpr bool operator<(const Q &lhs, const R &rhs)
Check whether a Quantity is less than another.
Definition units.h:337
constexpr Q operator*(Q quantity, Number multiple)
Multiply a Quantity by a Number.
Definition units.h:266
Quantity< std::ratio_add< typename Q1::mass, typename Q2::mass >, std::ratio_add< typename Q1::length, typename Q2::length >, std::ratio_add< typename Q1::time, typename Q2::time >, std::ratio_add< typename Q1::current, typename Q2::current >, std::ratio_add< typename Q1::angle, typename Q2::angle >, std::ratio_add< typename Q1::temperature, typename Q2::temperature >, std::ratio_add< typename Q1::luminosity, typename Q2::luminosity >, std::ratio_add< typename Q1::moles, typename Q2::moles > > Multiplied
Multiplying quantities adds their dimensions.
Definition units.h:194
void quantity_checker(Quantity< Mass, Length, Time, Current, Angle, Temperature, Luminosity, Moles >)
quantity checker. Used by the is_quantity concept
Definition units.h:170
constexpr bool operator>(const Q &lhs, const R &rhs)
Check whether a Quantity is greater than another.
Definition units.h:345
Quantity<> Number
Number is just dimensionless.
Definition units.h:162
constexpr auto to_angular(Q linear_distance, Length diameter)
Definition units.h:985