From 44cb96d50f713c5997dcdc3bae4b16c8f30b4900 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Oct 2025 13:58:09 +0100 Subject: [PATCH 01/13] Add comprehensive README documentation for Teensy 4.1 VCU system (#1) * Initial plan * Update README with comprehensive VCU system documentation for Teensy 4.1 Co-authored-by: swhelan123 <149515365+swhelan123@users.noreply.github.com> * Fix university name from UC Davis to University College Dublin Co-authored-by: swhelan123 <149515365+swhelan123@users.noreply.github.com> * Update README.md --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: swhelan123 <149515365+swhelan123@users.noreply.github.com> --- README.md | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index da2fd57..eddfb8d 100644 --- a/README.md +++ b/README.md @@ -1 +1,85 @@ -# TEENSY \ No newline at end of file +# TEENSY VCU System + +This repository contains the Vehicle Control Unit (VCU) software for the University College Dublin Formula SAE team, designed to run on the Teensy 4.1 microcontroller. + +## Overview + +This is the new repository for our VCU system. We are migrating to the Teensy 4.1 platform for improved performance and capabilities. The Teensy 4.1 provides: + +- 600 MHz ARM Cortex-M7 processor +- 8 MB Flash memory +- 1 MB RAM +- Multiple CAN bus interfaces +- High-speed serial communication +- Built-in Ethernet support + +## Project Structure + +``` +TEENSY/ +├── src/ # Source code files +│ └── main.cpp # Main application entry point +├── include/ # Header files +├── lib/ # Project-specific libraries +├── test/ # Unit tests +├── platformio.ini # PlatformIO configuration +└── README.md # This file +``` + +## Requirements + +- [PlatformIO](https://platformio.org/) - Cross-platform build system +- [Visual Studio Code](https://code.visualstudio.com/) (recommended) with PlatformIO IDE extension +- Teensy 4.1 board + +## Getting Started + +### Installation + +1. Install Visual Studio Code +2. Install the PlatformIO IDE extension +3. Clone this repository: + ```bash + git clone https://github.com/UCDFS/TEENSY.git + cd TEENSY + ``` + +### Building the Project + +1. Open the project folder in Visual Studio Code +2. PlatformIO will automatically detect the project and install dependencies +3. Build the project using one of these methods: + - Click the checkmark (✓) icon in the PlatformIO toolbar + - Use the command palette: `PlatformIO: Build` + - Run in terminal: `pio run` + +### Uploading to Teensy 4.1 + +1. Connect your Teensy 4.1 board via USB +2. Upload using one of these methods: + - Click the right arrow (→) icon in the PlatformIO toolbar + - Use the command palette: `PlatformIO: Upload` + - Run in terminal: `pio run --target upload` + +### Serial Monitor + +To view serial output from the Teensy: + +- Click the plug icon in the PlatformIO toolbar +- Use the command palette: `PlatformIO: Serial Monitor` +- Run in terminal: `pio device monitor` + +## Development + +### Adding Libraries + +Add library dependencies to `platformio.ini` under the `lib_deps` section: + +```ini +[env:teensy41] +platform = teensy +board = teensy41 +framework = arduino +lib_deps = + # Add libraries here +``` From bbc82a902cf4f6f0700c7c53ab217dd84051c882 Mon Sep 17 00:00:00 2001 From: cynosure Date: Tue, 4 Nov 2025 20:42:06 +0000 Subject: [PATCH 02/13] basic setup --- .../index/apps.cpp.4A4888B58627EC67.idx | Bin 0 -> 3006 bytes .../clangd/index/apps.h.E11F6CDFB32DD39D.idx | Bin 0 -> 2484 bytes .../index/header.h.AF8E12C7DE81203E.idx | Bin 0 -> 436 bytes .../index/main.cpp.C1463423FB8DE48E.idx | Bin 0 -> 1502 bytes .../warning_logger.h.86E8ECF88994B660.idx | Bin 0 -> 1876 bytes include/motor.h | 22 + include/motor_hal.h | 69 + include/units.h | 4969 +++++++++++++++++ src/motor_hal.cpp | 384 ++ 9 files changed, 5444 insertions(+) create mode 100644 .cache/clangd/index/apps.cpp.4A4888B58627EC67.idx create mode 100644 .cache/clangd/index/apps.h.E11F6CDFB32DD39D.idx create mode 100644 .cache/clangd/index/header.h.AF8E12C7DE81203E.idx create mode 100644 .cache/clangd/index/main.cpp.C1463423FB8DE48E.idx create mode 100644 .cache/clangd/index/warning_logger.h.86E8ECF88994B660.idx create mode 100644 include/motor.h create mode 100644 include/motor_hal.h create mode 100644 include/units.h create mode 100644 src/motor_hal.cpp diff --git a/.cache/clangd/index/apps.cpp.4A4888B58627EC67.idx b/.cache/clangd/index/apps.cpp.4A4888B58627EC67.idx new file mode 100644 index 0000000000000000000000000000000000000000..e2ea4fa05dfe7a2cd3dbd605cccdb52cadae9d95 GIT binary patch literal 3006 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 Oef_wRF968;zy1gFfh-#U literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/header.h.AF8E12C7DE81203E.idx b/.cache/clangd/index/header.h.AF8E12C7DE81203E.idx new file mode 100644 index 0000000000000000000000000000000000000000..f76d5ffebd730c93ce5df7d0e70e959327fdebd3 GIT binary patch literal 436 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 +#include +#include + +class MotorHAL { +public: + MotorHAL(); + MotorHAL(MotorHAL &&) = default; + MotorHAL(const MotorHAL &) = default; + MotorHAL &operator=(MotorHAL &&) = default; + MotorHAL &operator=(const MotorHAL &) = default; + ~MotorHAL(); +// ---------- BAMOCAR IDs ---------- +#define BAMOCAR_RX_ID 0x201 // Teensy → Bamocar +#define BAMOCAR_TX_ID 0x181 // Bamocar → Teensy + +// ---------- User constants ---------- +#define MAX_ACCEL_PERCENT 50 +#define TORQUE_MAX 32767 + const int chipSelect = BUILTIN_SDCARD; + // ---------- CAN setup ---------- + FlexCAN_T4 Can1; + + // ---------- Globals ---------- + File logFile; + int currentStep = 0; + int16_t currentTorque = 0; + uint32_t lastTorqueSend = 0; + bool bamocarOnline = false; + int rpmFeedback = 0; + int statusWord = 0; + float dcBusVoltage = 0.0; + + // ---------- Function prototypes ---------- + void executeStep(int step); + void readCanMessages(); + void sendCAN(const CAN_message_t &msg); + void requestStatusCyclic(uint8_t interval_ms); + void requestStatusOnce(); + void requestSpeedCyclic(uint8_t interval_ms); + void requestDCBusOnce(); + void clearErrors(); + void enableDrive(); + void disableDrive(); + void sendTorqueCommand(int16_t torqueValue); + void configureCanTimeout(uint16_t ms); + void updateTorqueFromPot(); + String interpretBamocarMessage(const CAN_message_t &msg); + void logCANFrame(const CAN_message_t &msg, const char *dir); + String generateFilename(); + void dumpLogToSerial(); + void sendTelemetry(); // new + void setup(); + void loop(); + +private: +}; + +MotorHAL::MotorHAL() {} + +MotorHAL::~MotorHAL() {} + +#endif // INCLUDE_INCLUDE_MOTOR_HAL_H_ diff --git a/include/units.h b/include/units.h new file mode 100644 index 0000000..de59cb3 --- /dev/null +++ b/include/units.h @@ -0,0 +1,4969 @@ +//-------------------------------------------------------------------------------------------------- +// +// Units: A compile-time c++14 unit conversion library with no dependencies +// +//-------------------------------------------------------------------------------------------------- +// +// The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software +// and associated documentation files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//-------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2016 Nic Holthaus +// +//-------------------------------------------------------------------------------------------------- +// +// ATTRIBUTION: +// Parts of this work have been adapted from: +// http://stackoverflow.com/questions/35069778/create-comparison-trait-for-template-classes-whose-parameters-are-in-a-different +// http://stackoverflow.com/questions/28253399/check-traits-for-all-variadic-template-arguments/28253503 +// http://stackoverflow.com/questions/36321295/rational-approximation-of-square-root-of-stdratio-at-compile-time?noredirect=1#comment60266601_36321295 +// +//-------------------------------------------------------------------------------------------------- +// +/// @file units.h +/// @brief Complete implementation of `units` - a compile-time, header-only, unit conversion +/// library built on c++14 with no dependencies. +// +//-------------------------------------------------------------------------------------------------- + +#pragma once + +#ifndef units_h__ +#define units_h__ + +#ifdef _MSC_VER +# pragma push_macro("pascal") +# undef pascal +# if _MSC_VER <= 1800 +# define _ALLOW_KEYWORD_MACROS +# pragma warning(push) +# pragma warning(disable : 4520) +# pragma push_macro("constexpr") +# define constexpr /*constexpr*/ +# pragma push_macro("noexcept") +# define noexcept throw() +# endif // _MSC_VER < 1800 +#endif // _MSC_VER + +#if defined(__MINGW64__) || defined(__MINGW32__) +# pragma push_macro("pascal") +# undef pascal +#endif // __MINGW64__ or __MINGW32__ + +#if !defined(_MSC_VER) || _MSC_VER > 1800 +# define UNIT_HAS_LITERAL_SUPPORT +# define UNIT_HAS_VARIADIC_TEMPLATE_SUPPORT +#endif + +#ifndef UNIT_LIB_DEFAULT_TYPE +# define UNIT_LIB_DEFAULT_TYPE double +#endif + +//-------------------- +// INCLUDES +//-------------------- + +#include +#include +#include +#include +#include +#include +#include + +#if !defined(UNIT_LIB_DISABLE_IOSTREAM) + #include + #include + #include + + //------------------------------ + // STRING FORMATTER + //------------------------------ + + namespace units + { + namespace detail + { + template std::string to_string(const T& t) + { + std::string str{ std::to_string(t) }; + int offset{ 1 }; + + // remove trailing decimal points for integer value units. Locale aware! + struct lconv * lc; + lc = localeconv(); + char decimalPoint = *lc->decimal_point; + if (str.find_last_not_of('0') == str.find(decimalPoint)) { offset = 0; } + str.erase(str.find_last_not_of('0') + offset, std::string::npos); + return str; + } + } + } +#endif + +namespace units +{ + template inline constexpr const char* name(const T&); + template inline constexpr const char* name_plural(const T&); + template inline constexpr const char* abbreviation(const T&); +} + +//------------------------------ +// MACROS +//------------------------------ + +/** + * @def UNIT_ADD_UNIT_TAGS(namespaceName,nameSingular, namePlural, abbreviation, definition) + * @brief Helper macro for generating the boiler-plate code generating the tags of a new unit. + * @details The macro generates singular, plural, and abbreviated forms + * of the unit definition (e.g. `meter`, `meters`, and `m`), as aliases for the + * unit tag. + * @param namespaceName namespace in which the new units will be encapsulated. + * @param nameSingular singular version of the unit name, e.g. 'meter' + * @param namePlural - plural version of the unit name, e.g. 'meters' + * @param abbreviation - abbreviated unit name, e.g. 'm' + * @param definition - the variadic parameter is used for the definition of the unit + * (e.g. `unit, units::category::length_unit>`) + * @note a variadic template is used for the definition to allow templates with + * commas to be easily expanded. All the variadic 'arguments' should together + * comprise the unit definition. + */ +#define UNIT_ADD_UNIT_TAGS(namespaceName,nameSingular, namePlural, abbreviation, /*definition*/...)\ + namespace namespaceName\ + {\ + /** @name Units (full names plural) */ /** @{ */ typedef __VA_ARGS__ namePlural; /** @} */\ + /** @name Units (full names singular) */ /** @{ */ typedef namePlural nameSingular; /** @} */\ + /** @name Units (abbreviated) */ /** @{ */ typedef namePlural abbreviation; /** @} */\ + } + +/** + * @def UNIT_ADD_UNIT_DEFINITION(namespaceName,nameSingular) + * @brief Macro for generating the boiler-plate code for the unit_t type definition. + * @details The macro generates the definition of the unit container types, e.g. `meter_t` + * @param namespaceName namespace in which the new units will be encapsulated. + * @param nameSingular singular version of the unit name, e.g. 'meter' + */ +#define UNIT_ADD_UNIT_DEFINITION(namespaceName,nameSingular)\ + namespace namespaceName\ + {\ + /** @name Unit Containers */ /** @{ */ typedef unit_t nameSingular ## _t; /** @} */\ + } + +/** + * @def UNIT_ADD_CUSTOM_TYPE_UNIT_DEFINITION(namespaceName,nameSingular,underlyingType) + * @brief Macro for generating the boiler-plate code for a unit_t type definition with a non-default underlying type. + * @details The macro generates the definition of the unit container types, e.g. `meter_t` + * @param namespaceName namespace in which the new units will be encapsulated. + * @param nameSingular singular version of the unit name, e.g. 'meter' + * @param underlyingType the underlying type + */ +#define UNIT_ADD_CUSTOM_TYPE_UNIT_DEFINITION(namespaceName,nameSingular, underlyingType)\ + namespace namespaceName\ + {\ + /** @name Unit Containers */ /** @{ */ typedef unit_t nameSingular ## _t; /** @} */\ + } +/** + * @def UNIT_ADD_IO(namespaceName,nameSingular, abbreviation) + * @brief Macro for generating the boiler-plate code needed for I/O for a new unit. + * @details The macro generates the code to insert units into an ostream. It + * prints both the value and abbreviation of the unit when invoked. + * @param namespaceName namespace in which the new units will be encapsulated. + * @param nameSingular singular version of the unit name, e.g. 'meter' + * @param abbrev - abbreviated unit name, e.g. 'm' + * @note When UNIT_LIB_DISABLE_IOSTREAM is defined, the macro does not generate any code + */ +#if defined(UNIT_LIB_DISABLE_IOSTREAM) + #define UNIT_ADD_IO(namespaceName, nameSingular, abbrev) +#else + #define UNIT_ADD_IO(namespaceName, nameSingular, abbrev)\ + namespace namespaceName\ + {\ + inline std::ostream& operator<<(std::ostream& os, const nameSingular ## _t& obj) \ + {\ + os << obj() << " "#abbrev; return os; \ + }\ + inline std::string to_string(const nameSingular ## _t& obj)\ + {\ + return units::detail::to_string(obj()) + std::string(" "#abbrev);\ + }\ + } +#endif + + /** + * @def UNIT_ADD_NAME(namespaceName,nameSingular,namePlural,abbreviation) + * @brief Macro for generating constexpr names/abbreviations for units. + * @details The macro generates names for units. E.g. name() of 1_m would be "meter", and + * abbreviation would be "m". + * @param namespaceName namespace in which the new units will be encapsulated. All literal values + * are placed in the `units::literals` namespace. + * @param nameSingular singular version of the unit name, e.g. 'meter' + * @param namePlural plural version of the unit name, e.g. 'meters' + * @param abbreviation - abbreviated unit name, e.g. 'm' + */ +#define UNIT_ADD_NAME(namespaceName, nameSingular, namePlural, abbrev)\ +template<> inline constexpr const char* name(const namespaceName::nameSingular ## _t&)\ +{\ + return #nameSingular;\ +}\ +template<> inline constexpr const char* abbreviation(const namespaceName::nameSingular ## _t&)\ +{\ + return #abbrev;\ +}\ +template<> inline constexpr const char* name_plural(const namespaceName::nameSingular ## _t&)\ +{\ + return #namePlural;\ +} + +/** + * @def UNIT_ADD_LITERALS(namespaceName,nameSingular,abbreviation) + * @brief Macro for generating user-defined literals for units. + * @details The macro generates user-defined literals for units. A literal suffix is created + * using the abbreviation (e.g. `10.0_m`). + * @param namespaceName namespace in which the new units will be encapsulated. All literal values + * are placed in the `units::literals` namespace. + * @param nameSingular singular version of the unit name, e.g. 'meter' + * @param abbreviation - abbreviated unit name, e.g. 'm' + * @note When UNIT_HAS_LITERAL_SUPPORT is not defined, the macro does not generate any code + */ +#if defined(UNIT_HAS_LITERAL_SUPPORT) + #define UNIT_ADD_LITERALS(namespaceName, nameSingular, abbreviation)\ + namespace literals\ + {\ + inline constexpr namespaceName::nameSingular ## _t operator""_ ## abbreviation(long double d)\ + {\ + return namespaceName::nameSingular ## _t(static_cast(d));\ + }\ + inline constexpr namespaceName::nameSingular ## _t operator""_ ## abbreviation (unsigned long long d)\ + {\ + return namespaceName::nameSingular ## _t(static_cast(d));\ + }\ + } +#else + #define UNIT_ADD_LITERALS(namespaceName, nameSingular, abbreviation) +#endif + +/** + * @def UNIT_ADD(namespaceName,nameSingular, namePlural, abbreviation, definition) + * @brief Macro for generating the boiler-plate code needed for a new unit. + * @details The macro generates singular, plural, and abbreviated forms + * of the unit definition (e.g. `meter`, `meters`, and `m`), as well as the + * appropriately named unit container (e.g. `meter_t`). A literal suffix is created + * using the abbreviation (e.g. `10.0_m`). It also defines a class-specific + * cout function which prints both the value and abbreviation of the unit when invoked. + * @param namespaceName namespace in which the new units will be encapsulated. All literal values + * are placed in the `units::literals` namespace. + * @param nameSingular singular version of the unit name, e.g. 'meter' + * @param namePlural - plural version of the unit name, e.g. 'meters' + * @param abbreviation - abbreviated unit name, e.g. 'm' + * @param definition - the variadic parameter is used for the definition of the unit + * (e.g. `unit, units::category::length_unit>`) + * @note a variadic template is used for the definition to allow templates with + * commas to be easily expanded. All the variadic 'arguments' should together + * comprise the unit definition. + */ +#define UNIT_ADD(namespaceName, nameSingular, namePlural, abbreviation, /*definition*/...)\ + UNIT_ADD_UNIT_TAGS(namespaceName,nameSingular, namePlural, abbreviation, __VA_ARGS__)\ + UNIT_ADD_UNIT_DEFINITION(namespaceName,nameSingular)\ + UNIT_ADD_NAME(namespaceName,nameSingular,namePlural, abbreviation)\ + UNIT_ADD_IO(namespaceName,nameSingular, abbreviation)\ + UNIT_ADD_LITERALS(namespaceName,nameSingular, abbreviation) + +/** + * @def UNIT_ADD_WITH_CUSTOM_TYPE(namespaceName,nameSingular, namePlural, abbreviation, underlyingType, definition) + * @brief Macro for generating the boiler-plate code needed for a new unit with a non-default underlying type. + * @details The macro generates singular, plural, and abbreviated forms + * of the unit definition (e.g. `meter`, `meters`, and `m`), as well as the + * appropriately named unit container (e.g. `meter_t`). A literal suffix is created + * using the abbreviation (e.g. `10.0_m`). It also defines a class-specific + * cout function which prints both the value and abbreviation of the unit when invoked. + * @param namespaceName namespace in which the new units will be encapsulated. All literal values + * are placed in the `units::literals` namespace. + * @param nameSingular singular version of the unit name, e.g. 'meter' + * @param namePlural - plural version of the unit name, e.g. 'meters' + * @param abbreviation - abbreviated unit name, e.g. 'm' + * @param underlyingType - the underlying type, e.g. 'int' or 'float' + * @param definition - the variadic parameter is used for the definition of the unit + * (e.g. `unit, units::category::length_unit>`) + * @note a variadic template is used for the definition to allow templates with + * commas to be easily expanded. All the variadic 'arguments' should together + * comprise the unit definition. + */ +#define UNIT_ADD_WITH_CUSTOM_TYPE(namespaceName, nameSingular, namePlural, abbreviation, underlyingType, /*definition*/...)\ + UNIT_ADD_UNIT_TAGS(namespaceName,nameSingular, namePlural, abbreviation, __VA_ARGS__)\ + UNIT_ADD_CUSTOM_TYPE_UNIT_DEFINITION(namespaceName,nameSingular,underlyingType)\ + UNIT_ADD_IO(namespaceName,nameSingular, abbreviation)\ + UNIT_ADD_LITERALS(namespaceName,nameSingular, abbreviation) + +/** + * @def UNIT_ADD_DECIBEL(namespaceName, nameSingular, abbreviation) + * @brief Macro to create decibel container and literals for an existing unit type. + * @details This macro generates the decibel unit container, cout overload, and literal definitions. + * @param namespaceName namespace in which the new units will be encapsulated. All literal values + * are placed in the `units::literals` namespace. + * @param nameSingular singular version of the base unit name, e.g. 'watt' + * @param abbreviation - abbreviated decibel unit name, e.g. 'dBW' + */ +#define UNIT_ADD_DECIBEL(namespaceName, nameSingular, abbreviation)\ + namespace namespaceName\ + {\ + /** @name Unit Containers */ /** @{ */ typedef unit_t abbreviation ## _t; /** @} */\ + }\ + UNIT_ADD_IO(namespaceName, abbreviation, abbreviation)\ + UNIT_ADD_LITERALS(namespaceName, abbreviation, abbreviation) + +/** + * @def UNIT_ADD_CATEGORY_TRAIT(unitCategory, baseUnit) + * @brief Macro to create the `is_category_unit` type trait. + * @details This trait allows users to test whether a given type matches + * an intended category. This macro comprises all the boiler-plate + * code necessary to do so. + * @param unitCategory The name of the category of unit, e.g. length or mass. + */ + +#define UNIT_ADD_CATEGORY_TRAIT_DETAIL(unitCategory)\ + namespace traits\ + {\ + /** @cond */\ + namespace detail\ + {\ + template struct is_ ## unitCategory ## _unit_impl : std::false_type {};\ + template\ + struct is_ ## unitCategory ## _unit_impl> : std::is_same>::base_unit_type>, units::category::unitCategory ## _unit>::type {};\ + template class N>\ + struct is_ ## unitCategory ## _unit_impl> : std::is_same>::unit_type>, units::category::unitCategory ## _unit>::type {};\ + }\ + /** @endcond */\ + } + +#if defined(UNIT_HAS_VARIADIC_TEMPLATE_SUPPORT) +#define UNIT_ADD_IS_UNIT_CATEGORY_TRAIT(unitCategory)\ + namespace traits\ + {\ + template struct is_ ## unitCategory ## _unit : std::integral_constant>::value...>::value> {};\ + } +#else +#define UNIT_ADD_IS_UNIT_CATEGORY_TRAIT(unitCategory)\ + namespace traits\ + {\ + template\ + struct is_ ## unitCategory ## _unit : std::integral_constant::type>::value &&\ + units::traits::detail::is_ ## unitCategory ## _unit_impl::type>::value &&\ + units::traits::detail::is_ ## unitCategory ## _unit_impl::type>::value>{};\ + } +#endif + +#define UNIT_ADD_CATEGORY_TRAIT(unitCategory)\ + UNIT_ADD_CATEGORY_TRAIT_DETAIL(unitCategory)\ + /** @ingroup TypeTraits*/\ + /** @brief Trait which tests whether a type represents a unit of unitCategory*/\ + /** @details Inherits from `std::true_type` or `std::false_type`. Use `is_ ## unitCategory ## _unit::value` to test the unit represents a unitCategory quantity.*/\ + /** @tparam T one or more types to test*/\ + UNIT_ADD_IS_UNIT_CATEGORY_TRAIT(unitCategory) + +/** + * @def UNIT_ADD_WITH_METRIC_PREFIXES(nameSingular, namePlural, abbreviation, definition) + * @brief Macro for generating the boiler-plate code needed for a new unit, including its metric + * prefixes from femto to peta. + * @details See UNIT_ADD. In addition to generating the unit definition and containers '(e.g. `meters` and 'meter_t', + * it also creates corresponding units with metric suffixes such as `millimeters`, and `millimeter_t`), as well as the + * literal suffixes (e.g. `10.0_mm`). + * @param namespaceName namespace in which the new units will be encapsulated. All literal values + * are placed in the `units::literals` namespace. + * @param nameSingular singular version of the unit name, e.g. 'meter' + * @param namePlural - plural version of the unit name, e.g. 'meters' + * @param abbreviation - abbreviated unit name, e.g. 'm' + * @param definition - the variadic parameter is used for the definition of the unit + * (e.g. `unit, units::category::length_unit>`) + * @note a variadic template is used for the definition to allow templates with + * commas to be easily expanded. All the variadic 'arguments' should together + * comprise the unit definition. + */ +#define UNIT_ADD_WITH_METRIC_PREFIXES(namespaceName, nameSingular, namePlural, abbreviation, /*definition*/...)\ + UNIT_ADD(namespaceName, nameSingular, namePlural, abbreviation, __VA_ARGS__)\ + UNIT_ADD(namespaceName, femto ## nameSingular, femto ## namePlural, f ## abbreviation, femto)\ + UNIT_ADD(namespaceName, pico ## nameSingular, pico ## namePlural, p ## abbreviation, pico)\ + UNIT_ADD(namespaceName, nano ## nameSingular, nano ## namePlural, n ## abbreviation, nano)\ + UNIT_ADD(namespaceName, micro ## nameSingular, micro ## namePlural, u ## abbreviation, micro)\ + UNIT_ADD(namespaceName, milli ## nameSingular, milli ## namePlural, m ## abbreviation, milli)\ + UNIT_ADD(namespaceName, centi ## nameSingular, centi ## namePlural, c ## abbreviation, centi)\ + UNIT_ADD(namespaceName, deci ## nameSingular, deci ## namePlural, d ## abbreviation, deci)\ + UNIT_ADD(namespaceName, deca ## nameSingular, deca ## namePlural, da ## abbreviation, deca)\ + UNIT_ADD(namespaceName, hecto ## nameSingular, hecto ## namePlural, h ## abbreviation, hecto)\ + UNIT_ADD(namespaceName, kilo ## nameSingular, kilo ## namePlural, k ## abbreviation, kilo)\ + UNIT_ADD(namespaceName, mega ## nameSingular, mega ## namePlural, M ## abbreviation, mega)\ + UNIT_ADD(namespaceName, giga ## nameSingular, giga ## namePlural, G ## abbreviation, giga)\ + UNIT_ADD(namespaceName, tera ## nameSingular, tera ## namePlural, T ## abbreviation, tera)\ + UNIT_ADD(namespaceName, peta ## nameSingular, peta ## namePlural, P ## abbreviation, peta)\ + + /** + * @def UNIT_ADD_WITH_METRIC_AND_BINARY_PREFIXES(nameSingular, namePlural, abbreviation, definition) + * @brief Macro for generating the boiler-plate code needed for a new unit, including its metric + * prefixes from femto to peta, and binary prefixes from kibi to exbi. + * @details See UNIT_ADD. In addition to generating the unit definition and containers '(e.g. `bytes` and 'byte_t', + * it also creates corresponding units with metric suffixes such as `millimeters`, and `millimeter_t`), as well as the + * literal suffixes (e.g. `10.0_B`). + * @param namespaceName namespace in which the new units will be encapsulated. All literal values + * are placed in the `units::literals` namespace. + * @param nameSingular singular version of the unit name, e.g. 'byte' + * @param namePlural - plural version of the unit name, e.g. 'bytes' + * @param abbreviation - abbreviated unit name, e.g. 'B' + * @param definition - the variadic parameter is used for the definition of the unit + * (e.g. `unit, units::category::data_unit>`) + * @note a variadic template is used for the definition to allow templates with + * commas to be easily expanded. All the variadic 'arguments' should together + * comprise the unit definition. + */ +#define UNIT_ADD_WITH_METRIC_AND_BINARY_PREFIXES(namespaceName, nameSingular, namePlural, abbreviation, /*definition*/...)\ + UNIT_ADD_WITH_METRIC_PREFIXES(namespaceName, nameSingular, namePlural, abbreviation, __VA_ARGS__)\ + UNIT_ADD(namespaceName, kibi ## nameSingular, kibi ## namePlural, Ki ## abbreviation, kibi)\ + UNIT_ADD(namespaceName, mebi ## nameSingular, mebi ## namePlural, Mi ## abbreviation, mebi)\ + UNIT_ADD(namespaceName, gibi ## nameSingular, gibi ## namePlural, Gi ## abbreviation, gibi)\ + UNIT_ADD(namespaceName, tebi ## nameSingular, tebi ## namePlural, Ti ## abbreviation, tebi)\ + UNIT_ADD(namespaceName, pebi ## nameSingular, pebi ## namePlural, Pi ## abbreviation, pebi)\ + UNIT_ADD(namespaceName, exbi ## nameSingular, exbi ## namePlural, Ei ## abbreviation, exbi) + +//-------------------- +// UNITS NAMESPACE +//-------------------- + +/** + * @namespace units + * @brief Unit Conversion Library namespace + */ +namespace units +{ + //---------------------------------- + // DOXYGEN + //---------------------------------- + + /** + * @defgroup UnitContainers Unit Containers + * @brief Defines a series of classes which contain dimensioned values. Unit containers + * store a value, and support various arithmetic operations. + */ + + /** + * @defgroup UnitTypes Unit Types + * @brief Defines a series of classes which represent units. These types are tags used by + * the conversion function, to create compound units, or to create `unit_t` types. + * By themselves, they are not containers and have no stored value. + */ + + /** + * @defgroup UnitManipulators Unit Manipulators + * @brief Defines a series of classes used to manipulate unit types, such as `inverse<>`, `squared<>`, and metric prefixes. + * Unit manipulators can be chained together, e.g. `inverse>>` to + * represent picoseconds^-2. + */ + + /** + * @defgroup CompileTimeUnitManipulators Compile-time Unit Manipulators + * @brief Defines a series of classes used to manipulate `unit_value_t` types at compile-time, such as `unit_value_add<>`, `unit_value_sqrt<>`, etc. + * Compile-time manipulators can be chained together, e.g. `unit_value_sqrt, unit_value_power>>` to + * represent `c = sqrt(a^2 + b^2). + */ + + /** + * @defgroup UnitMath Unit Math + * @brief Defines a collection of unit-enabled, strongly-typed versions of `` functions. + * @details Includes most c++11 extensions. + */ + + /** + * @defgroup Conversion Explicit Conversion + * @brief Functions used to convert values of one logical type to another. + */ + + /** + * @defgroup TypeTraits Type Traits + * @brief Defines a series of classes to obtain unit type information at compile-time. + */ + + //------------------------------ + // FORWARD DECLARATIONS + //------------------------------ + + /** @cond */ // DOXYGEN IGNORE + namespace constants + { + namespace detail + { + static constexpr const UNIT_LIB_DEFAULT_TYPE PI_VAL = 3.14159265358979323846264338327950288419716939937510; + } + } + /** @endcond */ // END DOXYGEN IGNORE + + //------------------------------ + // RATIO TRAITS + //------------------------------ + + /** + * @ingroup TypeTraits + * @{ + */ + + /** @cond */ // DOXYGEN IGNORE + namespace detail + { + /// has_num implementation. + template + struct has_num_impl + { + template + static constexpr auto test(U*)->std::is_integral {return std::is_integral{}; } + template + static constexpr std::false_type test(...) { return std::false_type{}; } + + using type = decltype(test(0)); + }; + } + + /** + * @brief Trait which checks for the existence of a static numerator. + * @details Inherits from `std::true_type` or `std::false_type`. Use `has_num::value` to test + * whether `class T` has a numerator static member. + */ + template + struct has_num : units::detail::has_num_impl::type {}; + + namespace detail + { + /// has_den implementation. + template + struct has_den_impl + { + template + static constexpr auto test(U*)->std::is_integral { return std::is_integral{}; } + template + static constexpr std::false_type test(...) { return std::false_type{}; } + + using type = decltype(test(0)); + }; + } + + /** + * @brief Trait which checks for the existence of a static denominator. + * @details Inherits from `std::true_type` or `std::false_type`. Use `has_den::value` to test + * whether `class T` has a denominator static member. + */ + template + struct has_den : units::detail::has_den_impl::type {}; + + /** @endcond */ // END DOXYGEN IGNORE + + namespace traits + { + /** + * @brief Trait that tests whether a type represents a std::ratio. + * @details Inherits from `std::true_type` or `std::false_type`. Use `is_ratio::value` to test + * whether `class T` implements a std::ratio. + */ + template + struct is_ratio : std::integral_constant::value && + has_den::value> + {}; + } + + //------------------------------ + // UNIT TRAITS + //------------------------------ + + /** @cond */ // DOXYGEN IGNORE + /** + * @brief void type. + * @details Helper class for creating type traits. + */ + template + struct void_t { typedef void type; }; + + /** + * @brief parameter pack for boolean arguments. + */ + template struct bool_pack {}; + + /** + * @brief Trait which tests that a set of other traits are all true. + */ + template + struct all_true : std::is_same, units::bool_pack> {}; + /** @endcond */ // DOXYGEN IGNORE + + /** + * @brief namespace representing type traits which can access the properties of types provided by the units library. + */ + namespace traits + { +#ifdef FOR_DOXYGEN_PURPOSES_ONLY + /** + * @ingroup TypeTraits + * @brief Traits class defining the properties of units. + * @details The units library determines certain properties of the units passed to + * them and what they represent by using the members of the corresponding + * unit_traits instantiation. + */ + template + struct unit_traits + { + typedef typename T::base_unit_type base_unit_type; ///< Unit type that the unit was derived from. May be a `base_unit` or another `unit`. Use the `base_unit_of` trait to find the SI base unit type. This will be `void` if type `T` is not a unit. + typedef typename T::conversion_ratio conversion_ratio; ///< `std::ratio` representing the conversion factor to the `base_unit_type`. This will be `void` if type `T` is not a unit. + typedef typename T::pi_exponent_ratio pi_exponent_ratio; ///< `std::ratio` representing the exponent of pi to be used in the conversion. This will be `void` if type `T` is not a unit. + typedef typename T::translation_ratio translation_ratio; ///< `std::ratio` representing a datum translation to the base unit (i.e. degrees C to degrees F conversion). This will be `void` if type `T` is not a unit. + }; +#endif + /** @cond */ // DOXYGEN IGNORE + /** + * @brief unit traits implementation for classes which are not units. + */ + template + struct unit_traits + { + typedef void base_unit_type; + typedef void conversion_ratio; + typedef void pi_exponent_ratio; + typedef void translation_ratio; + }; + + template + struct unit_traits + ::type> + { + typedef typename T::base_unit_type base_unit_type; ///< Unit type that the unit was derived from. May be a `base_unit` or another `unit`. Use the `base_unit_of` trait to find the SI base unit type. This will be `void` if type `T` is not a unit. + typedef typename T::conversion_ratio conversion_ratio; ///< `std::ratio` representing the conversion factor to the `base_unit_type`. This will be `void` if type `T` is not a unit. + typedef typename T::pi_exponent_ratio pi_exponent_ratio; ///< `std::ratio` representing the exponent of pi to be used in the conversion. This will be `void` if type `T` is not a unit. + typedef typename T::translation_ratio translation_ratio; ///< `std::ratio` representing a datum translation to the base unit (i.e. degrees C to degrees F conversion). This will be `void` if type `T` is not a unit. + }; + /** @endcond */ // END DOXYGEN IGNORE + } + + /** @cond */ // DOXYGEN IGNORE + namespace detail + { + /** + * @brief helper type to identify base units. + * @details A non-templated base class for `base_unit` which enables RTTI testing. + */ + struct _base_unit_t {}; + } + /** @endcond */ // END DOXYGEN IGNORE + + namespace traits + { + /** + * @ingroup TypeTraits + * @brief Trait which tests if a class is a `base_unit` type. + * @details Inherits from `std::true_type` or `std::false_type`. Use `is_base_unit::value` to test + * whether `class T` implements a `base_unit`. + */ + template + struct is_base_unit : std::is_base_of {}; + } + + /** @cond */ // DOXYGEN IGNORE + namespace detail + { + /** + * @brief helper type to identify units. + * @details A non-templated base class for `unit` which enables RTTI testing. + */ + struct _unit {}; + + template + using meter_ratio = std::ratio; + } + /** @endcond */ // END DOXYGEN IGNORE + + namespace traits + { + /** + * @ingroup TypeTraits + * @brief Traits which tests if a class is a `unit` + * @details Inherits from `std::true_type` or `std::false_type`. Use `is_unit::value` to test + * whether `class T` implements a `unit`. + */ + template + struct is_unit : std::is_base_of::type {}; + } + + /** @} */ // end of TypeTraits + + //------------------------------ + // BASE UNIT CLASS + //------------------------------ + + /** + * @ingroup UnitTypes + * @brief Class representing SI base unit types. + * @details Base units are represented by a combination of `std::ratio` template parameters, each + * describing the exponent of the type of unit they represent. Example: meters per second + * would be described by a +1 exponent for meters, and a -1 exponent for seconds, thus: + * `base_unit, std::ratio<0>, std::ratio<-1>>` + * @tparam Meter `std::ratio` representing the exponent value for meters. + * @tparam Kilogram `std::ratio` representing the exponent value for kilograms. + * @tparam Second `std::ratio` representing the exponent value for seconds. + * @tparam Radian `std::ratio` representing the exponent value for radians. Although radians are not SI base units, they are included because radians are described by the SI as m * m^-1, which would make them indistinguishable from scalars. + * @tparam Ampere `std::ratio` representing the exponent value for amperes. + * @tparam Kelvin `std::ratio` representing the exponent value for Kelvin. + * @tparam Mole `std::ratio` representing the exponent value for moles. + * @tparam Candela `std::ratio` representing the exponent value for candelas. + * @tparam Byte `std::ratio` representing the exponent value for bytes. + * @sa category for type aliases for SI base_unit types. + */ + template, + class Kilogram = std::ratio<0>, + class Second = std::ratio<0>, + class Radian = std::ratio<0>, + class Ampere = std::ratio<0>, + class Kelvin = std::ratio<0>, + class Mole = std::ratio<0>, + class Candela = std::ratio<0>, + class Byte = std::ratio<0>> + struct base_unit : units::detail::_base_unit_t + { + static_assert(traits::is_ratio::value, "Template parameter `Meter` must be a `std::ratio` representing the exponent of meters the unit has"); + static_assert(traits::is_ratio::value, "Template parameter `Kilogram` must be a `std::ratio` representing the exponent of kilograms the unit has"); + static_assert(traits::is_ratio::value, "Template parameter `Second` must be a `std::ratio` representing the exponent of seconds the unit has"); + static_assert(traits::is_ratio::value, "Template parameter `Ampere` must be a `std::ratio` representing the exponent of amperes the unit has"); + static_assert(traits::is_ratio::value, "Template parameter `Kelvin` must be a `std::ratio` representing the exponent of kelvin the unit has"); + static_assert(traits::is_ratio::value, "Template parameter `Candela` must be a `std::ratio` representing the exponent of candelas the unit has"); + static_assert(traits::is_ratio::value, "Template parameter `Mole` must be a `std::ratio` representing the exponent of moles the unit has"); + static_assert(traits::is_ratio::value, "Template parameter `Radian` must be a `std::ratio` representing the exponent of radians the unit has"); + static_assert(traits::is_ratio::value, "Template parameter `Byte` must be a `std::ratio` representing the exponent of bytes the unit has"); + + typedef Meter meter_ratio; + typedef Kilogram kilogram_ratio; + typedef Second second_ratio; + typedef Radian radian_ratio; + typedef Ampere ampere_ratio; + typedef Kelvin kelvin_ratio; + typedef Mole mole_ratio; + typedef Candela candela_ratio; + typedef Byte byte_ratio; + }; + + //------------------------------ + // UNIT CATEGORIES + //------------------------------ + + /** + * @brief namespace representing the implemented base and derived unit types. These will not generally be needed by library users. + * @sa base_unit for the definition of the category parameters. + */ + namespace category + { + // SCALAR (DIMENSIONLESS) TYPES + typedef base_unit<> scalar_unit; ///< Represents a quantity with no dimension. + typedef base_unit<> dimensionless_unit; ///< Represents a quantity with no dimension. + + // SI BASE UNIT TYPES + // METERS KILOGRAMS SECONDS RADIANS AMPERES KELVIN MOLE CANDELA BYTE --- CATEGORY + typedef base_unit> length_unit; ///< Represents an SI base unit of length + typedef base_unit, std::ratio<1>> mass_unit; ///< Represents an SI base unit of mass + typedef base_unit, std::ratio<0>, std::ratio<1>> time_unit; ///< Represents an SI base unit of time + typedef base_unit, std::ratio<0>, std::ratio<0>, std::ratio<1>> angle_unit; ///< Represents an SI base unit of angle + typedef base_unit, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>> current_unit; ///< Represents an SI base unit of current + typedef base_unit, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>> temperature_unit; ///< Represents an SI base unit of temperature + typedef base_unit, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>> substance_unit; ///< Represents an SI base unit of amount of substance + typedef base_unit, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>> luminous_intensity_unit; ///< Represents an SI base unit of luminous intensity + + // SI DERIVED UNIT TYPES + // METERS KILOGRAMS SECONDS RADIANS AMPERES KELVIN MOLE CANDELA BYTE --- CATEGORY + typedef base_unit, std::ratio<0>, std::ratio<0>, std::ratio<2>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>> solid_angle_unit; ///< Represents an SI derived unit of solid angle + typedef base_unit, std::ratio<0>, std::ratio<-1>> frequency_unit; ///< Represents an SI derived unit of frequency + typedef base_unit, std::ratio<0>, std::ratio<-1>> velocity_unit; ///< Represents an SI derived unit of velocity + typedef base_unit, std::ratio<0>, std::ratio<-1>, std::ratio<1>> angular_velocity_unit; ///< Represents an SI derived unit of angular velocity + typedef base_unit, std::ratio<0>, std::ratio<-2>> acceleration_unit; ///< Represents an SI derived unit of acceleration + typedef base_unit, std::ratio<0>, std::ratio<-3>> jerk_unit; ///< Represents an SI derived unit of jerk + typedef base_unit, std::ratio<1>, std::ratio<-2>> force_unit; ///< Represents an SI derived unit of force + typedef base_unit, std::ratio<1>, std::ratio<-2>> pressure_unit; ///< Represents an SI derived unit of pressure + typedef base_unit, std::ratio<0>, std::ratio<1>, std::ratio<0>, std::ratio<1>> charge_unit; ///< Represents an SI derived unit of charge + typedef base_unit, std::ratio<1>, std::ratio<-2>> energy_unit; ///< Represents an SI derived unit of energy + typedef base_unit, std::ratio<1>, std::ratio<-3>> power_unit; ///< Represents an SI derived unit of power + typedef base_unit, std::ratio<1>, std::ratio<-3>, std::ratio<0>, std::ratio<-1>> voltage_unit; ///< Represents an SI derived unit of voltage + typedef base_unit, std::ratio<-1>, std::ratio<4>, std::ratio<0>, std::ratio<2>> capacitance_unit; ///< Represents an SI derived unit of capacitance + typedef base_unit, std::ratio<1>, std::ratio<-3>, std::ratio<0>, std::ratio<-2>> impedance_unit; ///< Represents an SI derived unit of impedance + typedef base_unit, std::ratio<-1>, std::ratio<3>, std::ratio<0>, std::ratio<2>> conductance_unit; ///< Represents an SI derived unit of conductance + typedef base_unit, std::ratio<1>, std::ratio<-2>, std::ratio<0>, std::ratio<-1>> magnetic_flux_unit; ///< Represents an SI derived unit of magnetic flux + typedef base_unit, std::ratio<1>, std::ratio<-2>, std::ratio<0>, std::ratio<-1>> magnetic_field_strength_unit; ///< Represents an SI derived unit of magnetic field strength + typedef base_unit, std::ratio<1>, std::ratio<-2>, std::ratio<0>, std::ratio<-2>> inductance_unit; ///< Represents an SI derived unit of inductance + typedef base_unit, std::ratio<0>, std::ratio<0>, std::ratio<2>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>> luminous_flux_unit; ///< Represents an SI derived unit of luminous flux + typedef base_unit, std::ratio<0>, std::ratio<0>, std::ratio<2>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>> illuminance_unit; ///< Represents an SI derived unit of illuminance + typedef base_unit, std::ratio<0>, std::ratio<-1>> radioactivity_unit; ///< Represents an SI derived unit of radioactivity + + // OTHER UNIT TYPES + // METERS KILOGRAMS SECONDS RADIANS AMPERES KELVIN MOLE CANDELA BYTE --- CATEGORY + typedef base_unit, std::ratio<1>, std::ratio<-2>> torque_unit; ///< Represents an SI derived unit of torque + typedef base_unit> area_unit; ///< Represents an SI derived unit of area + typedef base_unit> volume_unit; ///< Represents an SI derived unit of volume + typedef base_unit, std::ratio<1>> density_unit; ///< Represents an SI derived unit of density + typedef base_unit<> concentration_unit; ///< Represents a unit of concentration + typedef base_unit, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>> data_unit; ///< Represents a unit of data size + typedef base_unit, std::ratio<0>, std::ratio<-1>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<1>> data_transfer_rate_unit; ///< Represents a unit of data transfer rate + } + + //------------------------------ + // UNIT CLASSES + //------------------------------ + + /** @cond */ // DOXYGEN IGNORE + /** + * @brief unit type template specialization for units derived from base units. + */ + template struct unit; + template + struct unit, PiExponent, Translation> : units::detail::_unit + { + static_assert(traits::is_ratio::value, "Template parameter `Conversion` must be a `std::ratio` representing the conversion factor to `BaseUnit`."); + static_assert(traits::is_ratio::value, "Template parameter `PiExponent` must be a `std::ratio` representing the exponents of Pi the unit has."); + static_assert(traits::is_ratio::value, "Template parameter `Translation` must be a `std::ratio` representing an additive translation required by the unit conversion."); + + typedef typename units::base_unit base_unit_type; + typedef Conversion conversion_ratio; + typedef Translation translation_ratio; + typedef PiExponent pi_exponent_ratio; + }; + /** @endcond */ // END DOXYGEN IGNORE + + /** + * @brief Type representing an arbitrary unit. + * @ingroup UnitTypes + * @details `unit` types are used as tags for the `conversion` function. They are *not* containers + * (see `unit_t` for a container class). Each unit is defined by: + * + * - A `std::ratio` defining the conversion factor to the base unit type. (e.g. `std::ratio<1,12>` for inches to feet) + * - A base unit that the unit is derived from (or a unit category. Must be of type `unit` or `base_unit`) + * - An exponent representing factors of PI required by the conversion. (e.g. `std::ratio<-1>` for a radians to degrees conversion) + * - a ratio representing a datum translation required for the conversion (e.g. `std::ratio<32>` for a fahrenheit to celsius conversion) + * + * Typically, a specific unit, like `meters`, would be implemented as a type alias + * of `unit`, i.e. `using meters = unit, units::category::length_unit`, or + * `using inches = unit, feet>`. + * @tparam Conversion std::ratio representing scalar multiplication factor. + * @tparam BaseUnit Unit type which this unit is derived from. May be a `base_unit`, or another `unit`. + * @tparam PiExponent std::ratio representing the exponent of pi required by the conversion. + * @tparam Translation std::ratio representing any datum translation required by the conversion. + */ + template, class Translation = std::ratio<0>> + struct unit : units::detail::_unit + { + static_assert(traits::is_unit::value, "Template parameter `BaseUnit` must be a `unit` type."); + static_assert(traits::is_ratio::value, "Template parameter `Conversion` must be a `std::ratio` representing the conversion factor to `BaseUnit`."); + static_assert(traits::is_ratio::value, "Template parameter `PiExponent` must be a `std::ratio` representing the exponents of Pi the unit has."); + + typedef typename units::traits::unit_traits::base_unit_type base_unit_type; + typedef typename std::ratio_multiply conversion_ratio; + typedef typename std::ratio_add pi_exponent_ratio; + typedef typename std::ratio_add, typename BaseUnit::translation_ratio> translation_ratio; + }; + + //------------------------------ + // BASE UNIT MANIPULATORS + //------------------------------ + + /** @cond */ // DOXYGEN IGNORE + namespace detail + { + /** + * @brief base_unit_of trait implementation + * @details recursively seeks base_unit type that a unit is derived from. Since units can be + * derived from other units, the `base_unit_type` typedef may not represent this value. + */ + template struct base_unit_of_impl; + template + struct base_unit_of_impl> : base_unit_of_impl {}; + template + struct base_unit_of_impl> + { + typedef base_unit type; + }; + template<> + struct base_unit_of_impl + { + typedef void type; + }; + } + /** @endcond */ // END DOXYGEN IGNORE + + namespace traits + { + /** + * @brief Trait which returns the `base_unit` type that a unit is originally derived from. + * @details Since units can be derived from other `unit` types in addition to `base_unit` types, + * the `base_unit_type` typedef will not always be a `base_unit` (or unit category). + * Since compatible + */ + template + using base_unit_of = typename units::detail::base_unit_of_impl::type; + } + + /** @cond */ // DOXYGEN IGNORE + namespace detail + { + /** + * @brief implementation of base_unit_multiply + * @details 'multiples' (adds exponent ratios of) two base unit types. Base units can be found + * using `base_unit_of`. + */ + template struct base_unit_multiply_impl; + template + struct base_unit_multiply_impl, base_unit> { + using type = base_unit...>; + }; + + /** + * @brief represents type of two base units multiplied together + */ + template + using base_unit_multiply = typename base_unit_multiply_impl::type; + + /** + * @brief implementation of base_unit_divide + * @details 'dived' (subtracts exponent ratios of) two base unit types. Base units can be found + * using `base_unit_of`. + */ + template struct base_unit_divide_impl; + template + struct base_unit_divide_impl, base_unit> { + using type = base_unit...>; + }; + + /** + * @brief represents the resulting type of `base_unit` U1 divided by U2. + */ + template + using base_unit_divide = typename base_unit_divide_impl::type; + + /** + * @brief implementation of inverse_base + * @details multiplies all `base_unit` exponent ratios by -1. The resulting type represents + * the inverse base unit of the given `base_unit` type. + */ + template struct inverse_base_impl; + + template + struct inverse_base_impl> { + using type = base_unit>...>; + }; + + /** + * @brief represent the inverse type of `class U` + * @details E.g. if `U` is `length_unit`, then `inverse` will represent `length_unit^-1`. + */ + template using inverse_base = typename inverse_base_impl::type; + + /** + * @brief implementation of `squared_base` + * @details multiplies all the exponent ratios of the given class by 2. The resulting type is + * equivalent to the given type squared. + */ + template struct squared_base_impl; + template + struct squared_base_impl> { + using type = base_unit>...>; + }; + + /** + * @brief represents the type of a `base_unit` squared. + * @details E.g. `squared` will represent `length_unit^2`. + */ + template using squared_base = typename squared_base_impl::type; + + /** + * @brief implementation of `cubed_base` + * @details multiplies all the exponent ratios of the given class by 3. The resulting type is + * equivalent to the given type cubed. + */ + template struct cubed_base_impl; + template + struct cubed_base_impl> { + using type = base_unit>...>; + }; + + /** + * @brief represents the type of a `base_unit` cubed. + * @details E.g. `cubed` will represent `length_unit^3`. + */ + template using cubed_base = typename cubed_base_impl::type; + + /** + * @brief implementation of `sqrt_base` + * @details divides all the exponent ratios of the given class by 2. The resulting type is + * equivalent to the square root of the given type. + */ + template struct sqrt_base_impl; + template + struct sqrt_base_impl> { + using type = base_unit>...>; + }; + + /** + * @brief represents the square-root type of a `base_unit`. + * @details E.g. `sqrt` will represent `length_unit^(1/2)`. + */ + template using sqrt_base = typename sqrt_base_impl::type; + + /** + * @brief implementation of `cbrt_base` + * @details divides all the exponent ratios of the given class by 3. The resulting type is + * equivalent to the given type's cube-root. + */ + template struct cbrt_base_impl; + template + struct cbrt_base_impl> { + using type = base_unit>...>; + }; + + /** + * @brief represents the cube-root type of a `base_unit` . + * @details E.g. `cbrt` will represent `length_unit^(1/3)`. + */ + template using cbrt_base = typename cbrt_base_impl::type; + } + /** @endcond */ // END DOXYGEN IGNORE + + //------------------------------ + // UNIT MANIPULATORS + //------------------------------ + + /** @cond */ // DOXYGEN IGNORE + namespace detail + { + /** + * @brief implementation of `unit_multiply`. + * @details multiplies two units. The base unit becomes the base units of each with their exponents + * added together. The conversion factors of each are multiplied by each other. Pi exponent ratios + * are added, and datum translations are removed. + */ + template + struct unit_multiply_impl + { + using type = unit < std::ratio_multiply, + base_unit_multiply , traits::base_unit_of>, + std::ratio_add, + std::ratio < 0 >> ; + }; + + /** + * @brief represents the type of two units multiplied together. + * @details recalculates conversion and exponent ratios at compile-time. + */ + template + using unit_multiply = typename unit_multiply_impl::type; + + /** + * @brief implementation of `unit_divide`. + * @details divides two units. The base unit becomes the base units of each with their exponents + * subtracted from each other. The conversion factors of each are divided by each other. Pi exponent ratios + * are subtracted, and datum translations are removed. + */ + template + struct unit_divide_impl + { + using type = unit < std::ratio_divide, + base_unit_divide, traits::base_unit_of>, + std::ratio_subtract, + std::ratio < 0 >> ; + }; + + /** + * @brief represents the type of two units divided by each other. + * @details recalculates conversion and exponent ratios at compile-time. + */ + template + using unit_divide = typename unit_divide_impl::type; + + /** + * @brief implementation of `inverse` + * @details inverts a unit (equivalent to 1/unit). The `base_unit` and pi exponents are all multiplied by + * -1. The conversion ratio numerator and denominator are swapped. Datum translation + * ratios are removed. + */ + template + struct inverse_impl + { + using type = unit < std::ratio, + inverse_base::base_unit_type>>, + std::ratio_multiply::pi_exponent_ratio, std::ratio<-1>>, + std::ratio < 0 >> ; // inverses are rates or change, the translation factor goes away. + }; + } + /** @endcond */ // END DOXYGEN IGNORE + + /** + * @brief represents the inverse unit type of `class U`. + * @ingroup UnitManipulators + * @tparam U `unit` type to invert. + * @details E.g. `inverse` will represent meters^-1 (i.e. 1/meters). + */ + template using inverse = typename units::detail::inverse_impl::type; + + /** @cond */ // DOXYGEN IGNORE + namespace detail + { + /** + * @brief implementation of `squared` + * @details Squares the conversion ratio, `base_unit` exponents, pi exponents, and removes + * datum translation ratios. + */ + template + struct squared_impl + { + static_assert(traits::is_unit::value, "Template parameter `Unit` must be a `unit` type."); + using Conversion = typename Unit::conversion_ratio; + using type = unit < std::ratio_multiply, + squared_base>, + std::ratio_multiply>, + typename Unit::translation_ratio + > ; + }; + } + /** @endcond */ // END DOXYGEN IGNORE + + /** + * @brief represents the unit type of `class U` squared + * @ingroup UnitManipulators + * @tparam U `unit` type to square. + * @details E.g. `square` will represent meters^2. + */ + template + using squared = typename units::detail::squared_impl::type; + + /** @cond */ // DOXYGEN IGNORE + namespace detail + { + /** + * @brief implementation of `cubed` + * @details Cubes the conversion ratio, `base_unit` exponents, pi exponents, and removes + * datum translation ratios. + */ + template + struct cubed_impl + { + static_assert(traits::is_unit::value, "Template parameter `Unit` must be a `unit` type."); + using Conversion = typename Unit::conversion_ratio; + using type = unit < std::ratio_multiply>, + cubed_base>, + std::ratio_multiply>, + typename Unit::translation_ratio> ; + }; + } + /** @endcond */ // END DOXYGEN IGNORE + + /** + * @brief represents the type of `class U` cubed. + * @ingroup UnitManipulators + * @tparam U `unit` type to cube. + * @details E.g. `cubed` will represent meters^3. + */ + template + using cubed = typename units::detail::cubed_impl::type; + + /** @cond */ // DOXYGEN IGNORE + namespace detail + { + //---------------------------------- + // RATIO_SQRT IMPLEMENTATION + //---------------------------------- + + using Zero = std::ratio<0>; + using One = std::ratio<1>; + template using Square = std::ratio_multiply; + + // Find the largest std::integer N such that Predicate::value is true. + template